zoukankan      html  css  js  c++  java
  • linux网络编程之socket编程(九)

    转眼又快到十一月份了,北京已经是完全进入冬天的节奏,外面冷风嗖嗖的,不过在夜深人静之时,学习永远成了我最快乐的时光,只有此时会觉得自己是如此踏实,虽说白天工作也是编一天程,但是此时的编程,是一种业余爱好,而非是生活所迫,非常享受它,好了,让享受继续。

    继上次学习了select函数,这次继续对它的用法进行进一步学习,下面就首先先来回顾一下该函数:

    上面是函数的原形,下面用理论阐述一下它,它可以看成是一个中心管理器,能够统一管理多个I/O,一旦其中的一个或多个I/O产生了我们所感兴趣的事件,就会被select检测到并返回,再来回顾一下参数,第一个参数是nfds,表示感兴趣的文件描述符的最大值+1,这里多解释一下,为什么是最大值+1呢?实际上select函数是在遍历它所感兴趣的文件描述符是否产生了事件,是从0开始遍历,至nfds的一个[0,nfds)的一个闭开区间,而通常我们写for循环时都会这样写:

    for(i=0; i<n; i++){

    //TODO

    }

    正好这也是一个闭开区间,所以第一个参数nfds为最大描述符+1,

    第二个参数readfds:可读事件集合

    第三个参数writefds:可写事件集合

    第四个参数exceptfds:异常事件集合

    第五个参数timeout:超时时间

    关于这些参数的详细描述,可以参考上篇博文:http://www.cnblogs.com/webor2006/p/4030126.html

    一旦检测到了多个事件,就需要一个一个遍历它,一一处理这些I/O事件,通常将用select实现的服务器称之为并发服务器。为啥叫并发服务器呢?因为当我们检测到多个I/O事件之后,实际上是无法并行处理这些I/O事件。实际上select处理事件是按顺序执行的,比如产生了三个事件,则是先执行第一个事件,然后再执行第二个事件,以此类推,所以说它不是并行的,而是并发,为啥是并发,是因为处理这些事件时间也不能太长,也就是说select无法实现并行处理,也就是无法充分利用多核CPU的特点,实际上对于单核的cpu来说,是根据没有并行可言的,而对于多核cpu,select是无法充分利用的,那这时该怎么办呢?可以采用多进程或多线程,关于并发与并行处理,这个之后会研究,这里先大致了解一下概念既可。

    上节中用select改进了回射客户端的问题,程序可以同时监测两种事件,一种是标准输入I/O事件,还一种是网络I/O,而不至于因为程序阻塞在标准输入I/O,而同时网络I/O也已经到达了而不能处理,这就是使用select的好处。

    上节中只使用了读条件了,这次会对其它事件也进行学习。

     

    以上是可读事件产生的四种情况。

    下面,就用select函数来改进回射服务器程序,上节只是改进了回射客户端程序。 

     先来回顾一下目前的服务器程序,是每连接成功一个客户端,就会创建一个子进程出来进行处理:

    这种服务器也叫做并发服务器,通过创建一个进程来达到并发的目的,当有多个客户端连接时,就会有多个进程,那有没有可能用一个进程来实现并发呢?当然是可以做到的,也就是用select,其最终原因是因为它能管理多个I/O,实际上,对于单核CPU来说,select处理并发并不会比多进程效率低,因为多进程在单核的情况下实际上还是按顺序来进行处理的,所以,下面则正式进行修改:

    首先将这些代码注释掉,因为是需要改成用select实现的:

    编写方法基本跟上节当中的客户端的差不多:

    【提示】:记得先记住这个allset,之后随着不断加入代码逻辑,就会自然而然显现它的作用了。

     

    接下来,由于事件成功返回了,那就可以判断标准输入listenfd是否在rset集合里,如果在集合中就证明已经检测到了事件,然后就可以分别进行判断处理了:

    另外,由于这一次是单进程的实现方式,当有多个客户端连接时,其conn客户端连接信息是需要用户个数组来保存的,

    int main(void)
    {
    /*    signal(SIGCHLD, SIG_IGN);*/
        signal(SIGCHLD, handle_sigchld);
        int listenfd;
        if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    /*    if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
            ERR_EXIT("socket");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5188);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
        /*inet_aton("127.0.0.1", &servaddr.sin_addr);*/
    
        int on = 1;
        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
            ERR_EXIT("setsockopt");
    
        if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("bind");
        if (listen(listenfd, SOMAXCONN) < 0)
            ERR_EXIT("listen");
    
        struct sockaddr_in peeraddr;
        socklen_t peerlen = sizeof(peeraddr);
        int conn;
    
        /*
        pid_t pid;
        while (1)
        {
            if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
                ERR_EXIT("accept");
    
            printf("ip=%s port=%d
    ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
    
            pid = fork();
            if (pid == -1)
                ERR_EXIT("fork");
            if (pid == 0)
            {
                close(listenfd);
                echo_srv(conn);
                exit(EXIT_SUCCESS);
            }
            else
                close(conn);
        }
        */
        
        int client[FD_SETSIZE];//定义一个数组用来保存conn,其中FD_SETSIZE为最大文件描述符个数,不能超过它
        int i;
        for (i=0; i<FD_SETSIZE; i++)//对里面的数组都初使化为-1
            client[i] = -1;
    
        int nready;//检测到的事件个数
        int maxfd = listenfd;//获取最大的文件描述符,目前listenfd最大
        
        fd_set rset;//声明一个可读的集合
        fd_set allset;
        //以下两句是将集合清空
        FD_ZERO(&rset);
        FD_ZERO(&allset);
        FD_SET(listenfd, &allset);//将监听套接口放到allset当中
        while (1)
        {
            rset = allset;
            nready = select(maxfd+1, &rset, NULL, NULL, NULL);
    
            if (nready == -1)
            {
                if (errno == EINTR)
                    continue;
                
                ERR_EXIT("select");
            }
            if (nready == 0)
                continue;
    
            if (FD_ISSET(listenfd, &rset))
            {
                peerlen = sizeof(peeraddr);
                conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
                if (conn == -1)
                    ERR_EXIT("accept");
    
                for (i=0; i<FD_SETSIZE; i++)//将conn放到数组当中
                {
                    if (client[i] < 0)
                    {
                        client[i] = conn;
                        break;
                    }
                }
    
                if (i == FD_SETSIZE)//没有找到空闲的位置,也就是连接数已经达到了上线,则给出提示
                {
                    fprintf(stderr, "too many clients
    ");
                    exit(EXIT_FAILURE);
                }
              printf("ip=%s port=%d
    ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));//打印输出对方的IP和端口信息
            }
        }
    
        return 0;
    }

    接下来要做一件事,这时已经得到了conn的套接口,下一次再调用select时我们也需要关心它的可读事件,这时需要做如下处理:

    那思考一下为什么要用到allset这个变量?这时因为rset会被select函数所改变,所以对于所有感兴趣的事件需要存放在allset当中,

    接下来,则处理已连接套接口事件了,对于这个套接口会有很多个,因为可以连接很多客户端,所以处理如下:

    int main(void)
    {
    /*    signal(SIGCHLD, SIG_IGN);*/
        signal(SIGCHLD, handle_sigchld);
        int listenfd;
        if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    /*    if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
            ERR_EXIT("socket");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5188);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
        /*inet_aton("127.0.0.1", &servaddr.sin_addr);*/
    
        int on = 1;
        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
            ERR_EXIT("setsockopt");
    
        if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("bind");
        if (listen(listenfd, SOMAXCONN) < 0)
            ERR_EXIT("listen");
    
        struct sockaddr_in peeraddr;
        socklen_t peerlen = sizeof(peeraddr);
        int conn;
        
        int client[FD_SETSIZE];//定义一个数组用来保存conn,其中FD_SETSIZE为最大文件描述符个数,不能超过它
        int i;
        for (i=0; i<FD_SETSIZE; i++)//对里面的数组都初使化为-1
            client[i] = -1;
    
        int nready;//检测到的事件个数
        int maxfd = listenfd;//获取最大的文件描述符,目前listenfd最大
        
        fd_set rset;//声明一个可读的集合
        fd_set allset;
        //以下两句是将集合清空
        FD_ZERO(&rset);
        FD_ZERO(&allset);
        FD_SET(listenfd, &allset);//将监听套接口放到allset当中
        while (1)
        {
            rset = allset;
            nready = select(maxfd+1, &rset, NULL, NULL, NULL);
    
            if (nready == -1)
            {
                if (errno == EINTR)
                    continue;
                
                ERR_EXIT("select");
            }
            if (nready == 0)
                continue;
    
            if (FD_ISSET(listenfd, &rset))
            {
                peerlen = sizeof(peeraddr);
                conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
                if (conn == -1)
                    ERR_EXIT("accept");
    
                for (i=0; i<FD_SETSIZE; i++)//将conn放到数组当中
                {
                    if (client[i] < 0)
                    {
                        client[i] = conn;
                        break;
                    }
                }
    
                if (i == FD_SETSIZE)//没有找到空闲的位置,也就是连接数已经达到了上线,则给出提示
                {
                    fprintf(stderr, "too many clients
    ");
                    exit(EXIT_FAILURE);
                }
                printf("ip=%s port=%d
    ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
                
                FD_SET(conn, &allset);
    
                if (--nready <= 0)
                    continue;
            }
    
            for (i=0; i<=FD_SETSIZE; i++)
            {
                conn = client[i];
                if (conn == -1)
                    continue;
    
                if (FD_ISSET(conn, &rset))
                {
                    char recvbuf[1024] = {0};
                    int ret = readline(conn, recvbuf, 1024);
                    if (ret == -1)
                        ERR_EXIT("readline");
                    if (ret == 0)
                    {//对方关闭
                        printf("client close
    ");
                        FD_CLR(conn, &allset);//从集合中将此已连接接口清除
                        client[i] = -1;//并且还原默认标识
                    }
    
                    fputs(recvbuf, stdout);
                    writen(conn, recvbuf, strlen(recvbuf));
    
                    if (--nready <= 0)
                        break;
                    
                }
            }
        }
    
        return 0;
    }

    另外,还需要关心一下最大描述符maxfd,当产生了新的连接套接口时,是需要将其进行更新的,于是修改代码如下:

    好了,下面来编译运行一下:

    可见,用单进程的方式也实现了多个客户端并发的处理。

    另外,此处程序还有可优化的地方,就是处理已连接事件的时候,总是遍历FD_SETSIZE,可以再加一个变量,用来记录最大的不空闲的i值,修改代码如下:

    int main(void)
    {
    /*    signal(SIGCHLD, SIG_IGN);*/
        signal(SIGCHLD, handle_sigchld);
        int listenfd;
        if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    /*    if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
            ERR_EXIT("socket");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5188);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
        /*inet_aton("127.0.0.1", &servaddr.sin_addr);*/
    
        int on = 1;
        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
            ERR_EXIT("setsockopt");
    
        if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("bind");
        if (listen(listenfd, SOMAXCONN) < 0)
            ERR_EXIT("listen");
    
        struct sockaddr_in peeraddr;
        socklen_t peerlen = sizeof(peeraddr);
        int conn;
        
        int client[FD_SETSIZE];//定义一个数组用来保存conn,其中FD_SETSIZE为最大文件描述符个数,不能超过它
        int maxi = 0;//用来记录最大的可连接套接口存放的位置
        int i;
        for (i=0; i<FD_SETSIZE; i++)//对里面的数组都初使化为-1
            client[i] = -1;
    
        int nready;//检测到的事件个数
        int maxfd = listenfd;//获取最大的文件描述符,目前listenfd最大
        
        fd_set rset;//声明一个可读的集合
        fd_set allset;
        //以下两句是将集合清空
        FD_ZERO(&rset);
        FD_ZERO(&allset);
        FD_SET(listenfd, &allset);//将监听套接口放到allset当中
        while (1)
        {
            rset = allset;
            nready = select(maxfd+1, &rset, NULL, NULL, NULL);
    
            if (nready == -1)
            {
                if (errno == EINTR)
                    continue;
                
                ERR_EXIT("select");
            }
            if (nready == 0)
                continue;
    
            if (FD_ISSET(listenfd, &rset))
            {
                peerlen = sizeof(peeraddr);
                conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
                if (conn == -1)
                    ERR_EXIT("accept");
    
                for (i=0; i<FD_SETSIZE; i++)//将conn放到数组当中
                {
                    if (client[i] < 0)
                    {
                        client[i] = conn;
                        if (i > maxi)
                            maxi = i;
                        break;
                    }
                }
    
                if (i == FD_SETSIZE)//没有找到空闲的位置,也就是连接数已经达到了上线,则给出提示
                {
                    fprintf(stderr, "too many clients
    ");
                    exit(EXIT_FAILURE);
                }
                printf("ip=%s port=%d
    ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
                
                FD_SET(conn, &allset);
                if (conn > maxfd)
                    maxfd = conn;
                
                if (--nready <= 0)
                    continue;
            }
    
            for (i=0; i<=maxi; i++)//这时就会减少循环的次数
            {
                conn = client[i];
                if (conn == -1)
                    continue;
    
                if (FD_ISSET(conn, &rset))
                {
                    char recvbuf[1024] = {0};
                    int ret = readline(conn, recvbuf, 1024);
                    if (ret == -1)
                        ERR_EXIT("readline");
                    if (ret == 0)
                    {//对方关闭
                        printf("client close
    ");
                        FD_CLR(conn, &allset);//从集合中将此已连接接口清除
                        client[i] = -1;//并且还原默认标识
                    }
    
                    fputs(recvbuf, stdout);
                    writen(conn, recvbuf, strlen(recvbuf));
    
                    if (--nready <= 0)
                        break;
                    
                }
            }
        }
    
        return 0;
    }

    最后再来看一下整个服务端的代码如下:

    echosrv.c:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <signal.h>
    #include <sys/wait.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    ssize_t readn(int fd, void *buf, size_t count)
    {
        size_t nleft = count;
        ssize_t nread;
        char *bufp = (char*)buf;
    
        while (nleft > 0)
        {
            if ((nread = read(fd, bufp, nleft)) < 0)
            {
                if (errno == EINTR)
                    continue;
                return -1;
            }
            else if (nread == 0)
                return count - nleft;
    
            bufp += nread;
            nleft -= nread;
        }
    
        return count;
    }
    
    ssize_t writen(int fd, const void *buf, size_t count)
    {
        size_t nleft = count;
        ssize_t nwritten;
        char *bufp = (char*)buf;
    
        while (nleft > 0)
        {
            if ((nwritten = write(fd, bufp, nleft)) < 0)
            {
                if (errno == EINTR)
                    continue;
                return -1;
            }
            else if (nwritten == 0)
                continue;
    
            bufp += nwritten;
            nleft -= nwritten;
        }
    
        return count;
    }
    
    ssize_t recv_peek(int sockfd, void *buf, size_t len)
    {
        while (1)
        {
            int ret = recv(sockfd, buf, len, MSG_PEEK);
            if (ret == -1 && errno == EINTR)
                continue;
            return ret;
        }
    }
    
    ssize_t readline(int sockfd, void *buf, size_t maxline)
    {
        int ret;
        int nread;
        char *bufp = buf;
        int nleft = maxline;
        while (1)
        {
            ret = recv_peek(sockfd, bufp, nleft);
            if (ret < 0)
                return ret;
            else if (ret == 0)
                return ret;
    
            nread = ret;
            int i;
            for (i=0; i<nread; i++)
            {
                if (bufp[i] == '
    ')
                {
                    ret = readn(sockfd, bufp, i+1);
                    if (ret != i+1)
                        exit(EXIT_FAILURE);
    
                    return ret;
                }
            }
    
            if (nread > nleft)
                exit(EXIT_FAILURE);
    
            nleft -= nread;
            ret = readn(sockfd, bufp, nread);
            if (ret != nread)
                exit(EXIT_FAILURE);
    
            bufp += nread;
        }
    
        return -1;
    }
    
    void echo_srv(int conn)
    {
        char recvbuf[1024];
            while (1)
            {
                    memset(recvbuf, 0, sizeof(recvbuf));
                    int ret = readline(conn, recvbuf, 1024);
            if (ret == -1)
                ERR_EXIT("readline");
            if (ret == 0)
            {
                printf("client close
    ");
                break;
            }
            
                    fputs(recvbuf, stdout);
                    writen(conn, recvbuf, strlen(recvbuf));
            }
    }
    
    void handle_sigchld(int sig)
    {
    /*    wait(NULL);*/
        while (waitpid(-1, NULL, WNOHANG) > 0)
            ;
    }
    
    int main(void)
    {
    /*    signal(SIGCHLD, SIG_IGN);*/
        signal(SIGCHLD, handle_sigchld);
        int listenfd;
        if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    /*    if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
            ERR_EXIT("socket");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5188);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
        /*inet_aton("127.0.0.1", &servaddr.sin_addr);*/
    
        int on = 1;
        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
            ERR_EXIT("setsockopt");
    
        if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("bind");
        if (listen(listenfd, SOMAXCONN) < 0)
            ERR_EXIT("listen");
    
        struct sockaddr_in peeraddr;
        socklen_t peerlen = sizeof(peeraddr);
        int conn;
        
        int client[FD_SETSIZE];//定义一个数组用来保存conn,其中FD_SETSIZE为最大文件描述符个数,不能超过它
        int maxi = 0;//用来记录最大的可连接套接口存放的位置
        int i;
        for (i=0; i<FD_SETSIZE; i++)//对里面的数组都初使化为-1
            client[i] = -1;
    
        int nready;//检测到的事件个数
        int maxfd = listenfd;//获取最大的文件描述符,目前listenfd最大
        
        fd_set rset;//声明一个可读的集合
        fd_set allset;
        //以下两句是将集合清空
        FD_ZERO(&rset);
        FD_ZERO(&allset);
        FD_SET(listenfd, &allset);//将监听套接口放到allset当中
        while (1)
        {
            rset = allset;
            nready = select(maxfd+1, &rset, NULL, NULL, NULL);
    
            if (nready == -1)
            {
                if (errno == EINTR)
                    continue;
                
                ERR_EXIT("select");
            }
            if (nready == 0)
                continue;
    
            if (FD_ISSET(listenfd, &rset))
            {
                peerlen = sizeof(peeraddr);
                conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
                if (conn == -1)
                    ERR_EXIT("accept");
    
                for (i=0; i<FD_SETSIZE; i++)//将conn放到数组当中
                {
                    if (client[i] < 0)
                    {
                        client[i] = conn;
                        if (i > maxi)
                            maxi = i;
                        break;
                    }
                }
    
                if (i == FD_SETSIZE)//没有找到空闲的位置,也就是连接数已经达到了上线,则给出提示
                {
                    fprintf(stderr, "too many clients
    ");
                    exit(EXIT_FAILURE);
                }
                printf("ip=%s port=%d
    ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
                
                FD_SET(conn, &allset);
                if (conn > maxfd)
                    maxfd = conn;
                
                if (--nready <= 0)
                    continue;
            }
    
            for (i=0; i<=maxi; i++)
            {
                conn = client[i];
                if (conn == -1)
                    continue;
    
                if (FD_ISSET(conn, &rset))
                {
                    char recvbuf[1024] = {0};
                    int ret = readline(conn, recvbuf, 1024);
                    if (ret == -1)
                        ERR_EXIT("readline");
                    if (ret == 0)
                    {//对方关闭
                        printf("client close
    ");
                        FD_CLR(conn, &allset);//从集合中将此已连接接口清除
                        client[i] = -1;//并且还原默认标识
                    }
    
                    fputs(recvbuf, stdout);
                    writen(conn, recvbuf, strlen(recvbuf));
    
                    if (--nready <= 0)
                        break;
                    
                }
            }
        }
    
        return 0;
    }

    echocli.c:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <signal.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    ssize_t readn(int fd, void *buf, size_t count)
    {
            size_t nleft = count;
            ssize_t nread;
            char *bufp = (char*)buf;
    
            while (nleft > 0)
            {
                    if ((nread = read(fd, bufp, nleft)) < 0)
                    {
                            if (errno == EINTR)
                                    continue;
                            return -1;
                    }
                    else if (nread == 0)
                            return count - nleft;
    
                    bufp += nread;
                    nleft -= nread;
            }
    
            return count;
    }
    
    ssize_t writen(int fd, const void *buf, size_t count)
    {
            size_t nleft = count;
            ssize_t nwritten;
            char *bufp = (char*)buf;
    
            while (nleft > 0)
            {
                    if ((nwritten = write(fd, bufp, nleft)) < 0)
                    {
                            if (errno == EINTR)
                                    continue;
                            return -1;
                    }
                    else if (nwritten == 0)
                            continue;
    
                    bufp += nwritten;
                    nleft -= nwritten;
            }
    
            return count;
    }
    
    ssize_t recv_peek(int sockfd, void *buf, size_t len)
    {
            while (1)
            {
                    int ret = recv(sockfd, buf, len, MSG_PEEK);
                    if (ret == -1 && errno == EINTR)
                            continue;
                    return ret;
            }
    }
    
    
    ssize_t readline(int sockfd, void *buf, size_t maxline)
    {
            int ret;
            int nread;
            char *bufp = buf;
            int nleft = maxline;
            while (1)
            {
                    ret = recv_peek(sockfd, bufp, nleft);
                    if (ret < 0)
                            return ret;
                    else if (ret == 0)
                            return ret;
    
                    nread = ret;
                    int i;
                    for (i=0; i<nread; i++)
                    {
                            if (bufp[i] == '
    ')
                            {
                                    ret = readn(sockfd, bufp, i+1);
                                    if (ret != i+1)
                                            exit(EXIT_FAILURE);
    
                                    return ret;
                            }
                    }
    
                    if (nread > nleft)
                            exit(EXIT_FAILURE);
    
                    nleft -= nread;
                    ret = readn(sockfd, bufp, nread);
                    if (ret != nread)
                            exit(EXIT_FAILURE);
    
                    bufp += nread;
            }
    
            return -1;
    }
    
    void echo_cli(int sock)
    {
    /*
        char sendbuf[1024] = {0};
            char recvbuf[1024] = {0};
            while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
            {
                    writen(sock, sendbuf, strlen(sendbuf));
    
                    int ret = readline(sock, recvbuf, sizeof(recvbuf));
                    if (ret == -1)
                            ERR_EXIT("readline");
                    else if (ret == 0)
                    {
                            printf("client close
    ");
                            break;
                    }
    
                    fputs(recvbuf, stdout);
                    memset(sendbuf, 0, sizeof(sendbuf));
                    memset(recvbuf, 0, sizeof(recvbuf));
            }
    
            close(sock);
    */
    
        fd_set rset;
        FD_ZERO(&rset);
    
        int nready;
        int maxfd;
        int fd_stdin = fileno(stdin);
        if (fd_stdin > sock)
            maxfd = fd_stdin;
        else
            maxfd = sock;
    
        char sendbuf[1024] = {0};
            char recvbuf[1024] = {0};
    
        while (1)
        {
            FD_SET(fd_stdin, &rset);
            FD_SET(sock, &rset);
            nready = select(maxfd+1, &rset, NULL, NULL, NULL);
            if (nready == -1)
                ERR_EXIT("select");
    
            if (nready == 0)
                continue;
    
            if (FD_ISSET(sock, &rset))
            {
                int ret = readline(sock, recvbuf, sizeof(recvbuf));
                        if (ret == -1)
                                ERR_EXIT("readline");
                        else if (ret == 0)
                        {
                                printf("server close
    ");
                                break;
                        }
    
                           fputs(recvbuf, stdout);
                        memset(recvbuf, 0, sizeof(recvbuf));
            }
            if (FD_ISSET(fd_stdin, &rset))
            {
                if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
                    break;
                writen(sock, sendbuf, strlen(sendbuf));
                memset(sendbuf, 0, sizeof(sendbuf));
            }
        }
    
        close(sock);
    }
    
    void handle_sigpipe(int sig)
    {
        printf("recv a sig=%d
    ", sig);
    }
    
    int main(void)
    {
    /*
        signal(SIGPIPE, handle_sigpipe);
    */
        signal(SIGPIPE, SIG_IGN);
        int sock;
        if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
            ERR_EXIT("socket");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5188);
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("connect");
    
        struct sockaddr_in localaddr;
        socklen_t addrlen = sizeof(localaddr);
        if (getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0)
            ERR_EXIT("getsockname");
    
        printf("ip=%s port=%d
    ", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));
    
    
        echo_cli(sock);
    
        return 0;
    }

    好了,今天就先学到这,下次继续。

  • 相关阅读:
    SharePoint 2010 User Profile Sync Service自动停止
    如何区别多个svchost.exe?
    Log Parser分析IIS log的一个简单例子
    Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
    Windows中右键点击文件夹, 结果找不到共享选项卡, 怎么办?
    介绍SOS中的SaveModule命令
    SharePoint中Draft版本的文档不会收到document added的Alert Email
    和我一起学Windows Workflow Foundation(1)创建和调试一个WF实例
    门户网站
    C#基础—— check、lock、using语句归纳
  • 原文地址:https://www.cnblogs.com/webor2006/p/4055284.html
Copyright © 2011-2022 走看看