1. 程式人生 > >unp第一章的測試程式碼

unp第一章的測試程式碼

下面是很有趣的一個測試,下面是客戶端的測試程式:

/*daytimetcpcli2.c*/
#include    "unp.h"

int
main(int argc, char **argv)
{
    int                 sockfd, n, counter = 0;
    char                recvline[MAXLINE + 1];
    struct sockaddr_in  servaddr;

    if (argc != 2)
        err_quit("usage: a.out <IPaddress>");

    if
( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err_sys("socket error"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(8088); /*這裡僅僅是埠號變化了 */ if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) err_quit("inet_pton error for %s"
, argv[1]); if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0) err_sys("connect error"); while ( (n = read(sockfd, recvline, MAXLINE)) > 0) { counter++; recvline[n] = 0; /* null terminate */ if (fputs(recvline, stdout) == EOF) err_sys("fputs error"
); } if (n < 0) err_sys("read error"); printf("counter = %d\n", counter); exit(0); }

然後是伺服器端的測試程式:

/*daytimesrv2.c*/
#include    "unp.h"
#include    <time.h>

int
main(int argc, char **argv)
{
    int                 listenfd, connfd, i;
    struct sockaddr_in  servaddr;
    char                buff[MAXLINE];
    time_t              ticks;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(8088);

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

    Listen(listenfd, LISTENQ);

    for ( ; ; ) {
        connfd = Accept(listenfd, (SA *) NULL, NULL);

        ticks = time(NULL);
        snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
        for (i = 0; i < strlen(buff); i++)
            Write(connfd, &buff[i], 1); //分多次寫

        Close(connfd);
    }
}

從上面我們可以看到,伺服器寫資料的時候多次寫,然而我們看一看在客戶端執行的結果,很有趣:
這裡寫圖片描述
我們是一次接收到的,這說明tcp存在一個緩衝區,並不是每一次的寫都會導致資料傳送,為了提高運輸的效率,寫的資料會儲存起來,滿了之後一次性發送

接下來是有關於客戶端和服務端的一些資訊的輸出,首先是客戶端:

/*daytimetcpcli3.c*/
#include    "unp.h"

int
main(int argc, char **argv)
{
    int                 sockfd, n;
    socklen_t           len;
    char                recvline[MAXLINE + 1];
    struct sockaddr_in  servaddr, cliaddr;

    if (argc != 2)
        err_quit("usage: a.out <IPaddress>");

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        err_sys("socket error");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(9999);  /* daytime server */
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
        err_quit("inet_pton error for %s", argv[1]);

    if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
        err_sys("connect error");

    len = sizeof(cliaddr);
    Getsockname(sockfd, (SA *) &cliaddr, &len);//得到sock的名字
    printf("local addr: %s\n",
           Sock_ntop((SA *) &cliaddr, sizeof(cliaddr)));//輸出關於伺服器的一些訊息

    while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
        recvline[n] = 0;    /* null terminate */
        if (fputs(recvline, stdout) == EOF)
            err_sys("fputs error");
    }
    if (n < 0)
        err_sys("read error");

    exit(0);
}

然後是關於伺服器的程式碼:

/*daytimetcpsrv3.c*/
#include    "unp.h"
#include    <time.h>

int
main(int argc, char **argv)
{
    int                 listenfd, connfd;
    socklen_t           len;
    struct sockaddr_in  servaddr, cliaddr;
    char                buff[MAXLINE];
    time_t              ticks;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(9999); /* daytime server */

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

    Listen(listenfd, LISTENQ);

    for ( ; ; ) {
        len = sizeof(cliaddr);
        connfd = Accept(listenfd, (SA *) &cliaddr, &len);
        printf("connection from %s, port %d\n",
               Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
               ntohs(cliaddr.sin_port));//輸出客戶端的資訊

        ticks = time(NULL);
        snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
        Write(connfd, buff, strlen(buff));

        Close(connfd);
    }
}

客戶端的執行情況:
這裡寫圖片描述
服務端的執行狀況:
這裡寫圖片描述

關於unp.h,由於該檔案太大,因此這裡只展示這裡需要的一小部分內容:

#ifndef __unp_h
#define __unp_h
#include    <sys/types.h>   /* basic system data types */
#include    <sys/socket.h>  /* basic socket definitions */

#include    <netinet/in.h>  /* sockaddr_in{} and other Internet defns */

#include    <errno.h>
#include    <fcntl.h>       /* for nonblocking */
#include    <netdb.h>
#include    <signal.h>
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>

#include    <unistd.h>
#include    <sys/wait.h>


/* Define bzero() as a macro if it's not in standard C library. */
#ifndef HAVE_BZERO
#define bzero(ptr,n)        memset(ptr, 0, n)
/* $$.If bzero$$ */
/* $$.If memset$$ */
#endif

/* Following could be derived from SOMAXCONN in <sys/socket.h>, but many
   kernels still #define it as 5, while actually supporting many more */
#define LISTENQ     1024    /* 2nd argument to listen() */
#define MAXLINE     4096    /* max text line length */
/* Following shortens all the typecasts of pointer arguments: */
#define SA  struct sockaddr


void     Write(int, void *, size_t);
int      Accept(int, SA *, socklen_t *);
void     Bind(int, const SA *, socklen_t);
void     Connect(int, const SA *, socklen_t);
void     Listen(int, int);
ssize_t  Read(int fd, void *ptr, size_t nbytes);
#endif

下面是用到的一些包裹函式,之所以使用包裹函式,是為了減少一個函式體內的程式碼量,包裹了之後可以在一個函式體內減少很多的出錯處理,使程式碼變得簡單和優雅。

/* include Listen */
void
Listen(int fd, int backlog)
{
    char    *ptr;

        /*4can override 2nd argument with environment variable */
    if ( (ptr = getenv("LISTENQ")) != NULL)
        backlog = atoi(ptr);

    if (listen(fd, backlog) < 0)
        err_sys("listen error");
}

ssize_t
Recv(int fd, void *ptr, size_t nbytes, int flags)
{
    ssize_t     n;

    if ( (n = recv(fd, ptr, nbytes, flags)) < 0)
        err_sys("recv error");
    return(n);
}

int
Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
    int     n;

again:
    if ( (n = accept(fd, sa, salenptr)) < 0) {
#ifdef  EPROTO
        if (errno == EPROTO || errno == ECONNABORTED)
#else
        if (errno == ECONNABORTED)
#endif
            goto again;
        else
            err_sys("accept error");
    }
    return(n);
}

void
Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if (bind(fd, sa, salen) < 0)
        err_sys("bind error");
}

void
Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if (connect(fd, sa, salen) < 0)
        err_sys("connect error");
}


ssize_t
Read(int fd, void *ptr, size_t nbytes)
{
    ssize_t     n;

    if ( (n = read(fd, ptr, nbytes)) == -1)
        err_sys("read error");
    return(n);
}

void
Write(int fd, void *ptr, size_t nbytes)
{
    if (write(fd, ptr, nbytes) != nbytes)
        err_sys("write error");
}

程式碼量有點大,先到這裡吧!