Andy Niu �����ĵ�

Andy Niu

Andy Niu Help  1.0.0.0
IO多路复用

变量

 IO多路复用__总结
 
 IO多路复用__select
 
 IO多路复用__poll
 
 IO多路复用__epoll
 

详细描述

变量说明

IO多路复用__epoll
1、server.cpp
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <sys/epoll.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    #define IPADDRESS   "127.0.0.1"
    #define PORT        8787
    #define MAXSIZE     1024
    #define LISTENQ     5
    #define FDSIZE      1000
    #define EPOLLEVENTS 100
    
    //函数声明
    //创建套接字并进行绑定
    static int socket_bind(const char* ip,int port);
    //IO多路复用epoll
    static void do_epoll(int listenfd);
    //事件处理函数
    static void
    handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf);
    //处理接收到的连接
    static void handle_accpet(int epollfd,int listenfd);
    //读处理
    static void do_read(int epollfd,int fd,char *buf);
    //写处理
    static void do_write(int epollfd,int fd,char *buf);
    //添加事件
    static void add_event(int epollfd,int fd,int state);
    //修改事件
    static void modify_event(int epollfd,int fd,int state);
    //删除事件
    static void delete_event(int epollfd,int fd,int state);
    
    int main(int argc,char *argv[])
    {
        int  listenfd;
        listenfd = socket_bind(IPADDRESS,PORT);
        listen(listenfd,LISTENQ);
        do_epoll(listenfd);
        return 0;
    }
    
    static int socket_bind(const char* ip,int port)
    {
        int  listenfd;
        struct sockaddr_in servaddr;
        listenfd = socket(AF_INET,SOCK_STREAM,0);
        if (listenfd == -1)
        {
            perror("socket error:");
            exit(1);
        }
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        inet_pton(AF_INET,ip,&servaddr.sin_addr);
        servaddr.sin_port = htons(port);
        if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
        {
            perror("bind error: ");
            exit(1);
        }
        return listenfd;
    }
    
    static void do_epoll(int listenfd)
    {
        int epollfd;
        struct epoll_event events[EPOLLEVENTS];
        int ret;
        char buf[MAXSIZE];
        memset(buf,0,MAXSIZE);
        //创建一个描述符
        epollfd = epoll_create(FDSIZE);
        //添加监听描述符事件
        add_event(epollfd,listenfd,EPOLLIN);
        for ( ; ; )
        {
            //获取已经准备好的描述符事件
            ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1);
            handle_events(epollfd,events,ret,listenfd,buf);
        }
        close(epollfd);
    }
    
    static void
    handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf)
    {
        int i;
        int fd;
        //进行选好遍历
        for (i = 0;i < num;i++)
        {
            fd = events[i].data.fd;
            //根据描述符的类型和事件类型进行处理
            if ((fd == listenfd) &&(events[i].events & EPOLLIN))
                handle_accpet(epollfd,listenfd);
            else if (events[i].events & EPOLLIN)
                do_read(epollfd,fd,buf);
            else if (events[i].events & EPOLLOUT)
                do_write(epollfd,fd,buf);
        }
    }
    static void handle_accpet(int epollfd,int listenfd)
    {
        int clifd;
        struct sockaddr_in cliaddr;
        socklen_t  cliaddrlen;
        clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
        if (clifd == -1)
            perror("accpet error:");
        else
        {
            // 注意:cliaddr.sin_port的字节逆序,需要转换一下ntohs(cliaddr.sin_port)
            printf("accept a new client: %s:%d\n",
                inet_ntoa(cliaddr.sin_addr),
                ntohs(cliaddr.sin_port));
            //添加一个客户描述符和事件
            add_event(epollfd,clifd,EPOLLIN);
        }
    }
    
    static void do_read(int epollfd,int fd,char *buf)
    {
        int nread;
        nread = read(fd,buf,MAXSIZE);
        if (nread == -1)
        {
            perror("read error:");
            close(fd);
            delete_event(epollfd,fd,EPOLLIN);
        }
        else if (nread == 0)
        {
            fprintf(stderr,"client close.\n");
            close(fd);
            delete_event(epollfd,fd,EPOLLIN);
        }
        else
        {
            printf("read message is : %s",buf);
            //修改描述符对应的事件,由读改为写
            modify_event(epollfd,fd,EPOLLOUT);
        }
    }
    
    static void do_write(int epollfd,int fd,char *buf)
    {
        int nwrite;
        nwrite = write(fd,buf,strlen(buf));
        if (nwrite == -1)
        {
            perror("write error:");
            close(fd);
            delete_event(epollfd,fd,EPOLLOUT);
        }
        else
            modify_event(epollfd,fd,EPOLLIN);
        memset(buf,0,MAXSIZE);
    }
    
    static void add_event(int epollfd,int fd,int state)
    {
        struct epoll_event ev;
        ev.events = state;
        ev.data.fd = fd;
        epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
    }
    
    static void delete_event(int epollfd,int fd,int state)
    {
        struct epoll_event ev;
        ev.events = state;
        ev.data.fd = fd;
        epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
    }
    
    static void modify_event(int epollfd,int fd,int state)
    {
        struct epoll_event ev;
        ev.events = state;
        ev.data.fd = fd;
        epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
    }
2、client.cpp
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sys/epoll.h>
    #include <time.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    
    #define MAXSIZE     1024
    #define IPADDRESS   "127.0.0.1"
    #define SERV_PORT   8787
    #define FDSIZE        1024
    #define EPOLLEVENTS 20
    
    static void handle_connection(int sockfd);
    static void
    handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf);
    static void do_read(int epollfd,int fd,int sockfd,char *buf);
    static void do_read(int epollfd,int fd,int sockfd,char *buf);
    static void do_write(int epollfd,int fd,int sockfd,char *buf);
    static void add_event(int epollfd,int fd,int state);
    static void delete_event(int epollfd,int fd,int state);
    static void modify_event(int epollfd,int fd,int state);
    
    int main(int argc,char *argv[])
    {
        int                 sockfd;
        struct sockaddr_in  servaddr;
        sockfd = socket(AF_INET,SOCK_STREAM,0);
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(SERV_PORT);
        inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);
        connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
        //处理连接
        handle_connection(sockfd);
        close(sockfd);
        return 0;
    }
    
    
    static void handle_connection(int sockfd)
    {
        int epollfd;
        struct epoll_event events[EPOLLEVENTS];
        char buf[MAXSIZE];
        int ret;
        epollfd = epoll_create(FDSIZE);
        add_event(epollfd,STDIN_FILENO,EPOLLIN);
        for ( ; ; )
        {
            ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1);
            handle_events(epollfd,events,ret,sockfd,buf);
        }
        close(epollfd);
    }
    
    static void
    handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf)
    {
        int fd;
        int i;
        for (i = 0;i < num;i++)
        {
            fd = events[i].data.fd;
            if (events[i].events & EPOLLIN)
                do_read(epollfd,fd,sockfd,buf);
            else if (events[i].events & EPOLLOUT)
                do_write(epollfd,fd,sockfd,buf);
        }
    }
    
    static void do_read(int epollfd,int fd,int sockfd,char *buf)
    {
        int nread;
        nread = read(fd,buf,MAXSIZE);
        if (nread == -1)
        {
            perror("read error:");
            close(fd);
        }
        else if (nread == 0)
        {
            fprintf(stderr,"server close.\n");
            close(fd);
        }
        else
        {
            if (fd == STDIN_FILENO)
                add_event(epollfd,sockfd,EPOLLOUT);
            else
            {
                delete_event(epollfd,sockfd,EPOLLIN);
                add_event(epollfd,STDOUT_FILENO,EPOLLOUT);
            }
        }
    }
    
    static void do_write(int epollfd,int fd,int sockfd,char *buf)
    {
        int nwrite;
        nwrite = write(fd,buf,strlen(buf));
        if (nwrite == -1)
        {
            perror("write error:");
            close(fd);
        }
        else
        {
            if (fd == STDOUT_FILENO)
                delete_event(epollfd,fd,EPOLLOUT);
            else
                modify_event(epollfd,fd,EPOLLIN);
        }
        memset(buf,0,MAXSIZE);
    }
    
    static void add_event(int epollfd,int fd,int state)
    {
        struct epoll_event ev;
        ev.events = state;
        ev.data.fd = fd;
        epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
    }
    
    static void delete_event(int epollfd,int fd,int state)
    {
        struct epoll_event ev;
        ev.events = state;
        ev.data.fd = fd;
        epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
    }
    
    static void modify_event(int epollfd,int fd,int state)
    {
        struct epoll_event ev;
        ev.events = state;
        ev.data.fd = fd;
        epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
    }
3、运行服务端
    [root@localhost Epoll]# g++ -o server server.cpp
    server.cpp:190:2: warning: no newline at end of file
    [root@localhost Epoll]# ./server
    accept a new client: 127.0.0.1:51619
    read message is : 123
4、运行客户端
    [root@localhost Epoll]# g++ -o client client.cpp
    client.cpp:143:2: warning: no newline at end of file
    [root@localhost Epoll]# ./client
    123
    123
IO多路复用__poll
1、server.cpp
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <poll.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    
    #define IPADDRESS   "127.0.0.1"
    #define PORT        8787
    #define MAXLINE     1024
    #define LISTENQ     5
    #define OPEN_MAX    1000
    #define INFTIM      -1
    
    //函数声明
    //创建套接字并进行绑定
    static int socket_bind(const char* ip,int port);
    //IO多路复用poll
    static void do_poll(int listenfd);
    //处理多个连接
    static void handle_connection(struct pollfd *connfds,int num);
    
    int main(int argc,char *argv[])
    {
        int  listenfd,connfd,sockfd;
        struct sockaddr_in cliaddr;
        socklen_t cliaddrlen;
        listenfd = socket_bind(IPADDRESS,PORT);
        listen(listenfd,LISTENQ);
        do_poll(listenfd);
        return 0;
    }
    
    static int socket_bind(const char* ip,int port)
    {
        int  listenfd;
        struct sockaddr_in servaddr;
        listenfd = socket(AF_INET,SOCK_STREAM,0);
        if (listenfd == -1)
        {
            perror("socket error:");
            exit(1);
        }
    
        int reuse = 1;
        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
            return -1;
        }
    
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        inet_pton(AF_INET,ip,&servaddr.sin_addr);
        servaddr.sin_port = htons(port);
        if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
        {
            perror("bind error: ");
            exit(1);
        }
        return listenfd;
    }
    
    static void do_poll(int listenfd)
    {
        int  connfd,sockfd;
        struct sockaddr_in cliaddr;
        socklen_t cliaddrlen;
        struct pollfd clientfds[OPEN_MAX];
        int maxi;
        int i;
        int nready;
        //添加监听描述符
        clientfds[0].fd = listenfd;
        clientfds[0].events = POLLIN;
        //初始化客户连接描述符
        for (i = 1;i < OPEN_MAX;i++)
            clientfds[i].fd = -1;
        maxi = 0;
        //循环处理
        for ( ; ; )
        {
            //获取可用描述符的个数
            nready = poll(clientfds,maxi+1,INFTIM);
            if (nready == -1)
            {
                perror("poll error:");
                exit(1);
            }
            //测试监听描述符是否准备好
            if (clientfds[0].revents & POLLIN)
            {
                cliaddrlen = sizeof(cliaddr);
                //接受新的连接
                if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1)
                {
                    if (errno == EINTR)
                        continue;
                    else
                    {
                        perror("accept error:");
                        exit(1);
                    }
                }
                fprintf(stdout,"accept a new client: %s:%d\n", 
                    inet_ntoa(cliaddr.sin_addr),
                    ntohs(cliaddr.sin_port));
                //将新的连接描述符添加到数组中
                for (i = 1;i < OPEN_MAX;i++)
                {
                    if (clientfds[i].fd < 0)
                    {
                        clientfds[i].fd = connfd;
                        break;
                    }
                }
                if (i == OPEN_MAX)
                {
                    fprintf(stderr,"too many clients.\n");
                    exit(1);
                }
                //将新的描述符添加到读描述符集合中
                clientfds[i].events = POLLIN;
                //记录客户连接套接字的个数
                maxi = (i > maxi ? i : maxi);
                if (--nready <= 0)
                    continue;
            }
            //处理客户连接
            handle_connection(clientfds,maxi);
        }
    }
    
    static void handle_connection(struct pollfd *connfds,int num)
    {
        int i,n;
        char buf[MAXLINE];
        memset(buf,0,MAXLINE);
        for (i = 1;i <= num;i++)
        {
            if (connfds[i].fd < 0)
                continue;
            //测试客户描述符是否准备好
            if (connfds[i].revents & POLLIN)
            {
                //接收客户端发送的信息
                n = read(connfds[i].fd,buf,MAXLINE);
                if (n == 0)
                {
                    close(connfds[i].fd);
                    connfds[i].fd = -1;
                    continue;
                }
                // printf("read msg is: ");
                write(STDOUT_FILENO,buf,n);
                //向客户端发送buf
                write(connfds[i].fd,buf,n);
            }
        }
    }
2、client.cpp
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <poll.h>
    #include <time.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    
    #define MAXLINE     1024
    #define IPADDRESS   "127.0.0.1"
    #define SERV_PORT   8787
    
    #define max(a,b) (a > b) ? a : b
    
    static void handle_connection(int sockfd);
    
    int main(int argc,char *argv[])
    {
        int                 sockfd;
        struct sockaddr_in  servaddr;
        sockfd = socket(AF_INET,SOCK_STREAM,0);
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(SERV_PORT);
        inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);
        connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
        //处理连接描述符
        handle_connection(sockfd);
        return 0;
    }
    
    static void handle_connection(int sockfd)
    {
        char    sendline[MAXLINE],recvline[MAXLINE];
        int     maxfdp,stdineof;
        struct pollfd pfds[2];
        int n;
        //添加连接描述符
        pfds[0].fd = sockfd;
        pfds[0].events = POLLIN;
        //添加标准输入描述符
        pfds[1].fd = STDIN_FILENO;
        pfds[1].events = POLLIN;
        for (; ;)
        {
            poll(pfds,2,-1);
            if (pfds[0].revents & POLLIN)
            {
                n = read(sockfd,recvline,MAXLINE);
                if (n == 0)
                {
                    fprintf(stderr,"client: server is closed.\n");
                    close(sockfd);
                }
                write(STDOUT_FILENO,recvline,n);
            }
            //测试标准输入是否准备好
            if (pfds[1].revents & POLLIN)
            {
                n = read(STDIN_FILENO,sendline,MAXLINE);
                if (n  == 0)
                {
                    shutdown(sockfd,SHUT_WR);
                    continue;
                }
                write(sockfd,sendline,n);
            }
        }
    }
3、运行服务端
    [root@localhost poll]# ./server
    accept a new client: 127.0.0.1:54849
    abc
4、运行客户端
    [root@localhost poll]# ./client
    abc
    abc
IO多路复用__select
1、server.cpp
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <sys/select.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <assert.h>
    
    #define IPADDR      "127.0.0.1"
    #define PORT        8787
    #define MAXLINE     1024
    #define LISTENQ     5
    #define SIZE        10
    
    typedef struct server_context_st
    {
        int cli_cnt;        /*客户端个数*/
        int clifds[SIZE];   /*客户端的个数*/
        fd_set allfds;      /*句柄集合*/
        int maxfd;          /*句柄最大值*/
    } server_context_st;
    static server_context_st *s_srv_ctx = NULL;
    /*===========================================================================
    * ==========================================================================*/
    static int create_server_proc(const char* ip,int port)
    {
        int  fd;
        struct sockaddr_in servaddr;
        fd = socket(AF_INET, SOCK_STREAM,0);
        if (fd == -1) {
            fprintf(stderr, "create socket fail,erron:%d,reason:%s\n",
                errno, strerror(errno));
            return -1;
        }
    
        /*一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用。*/
        int reuse = 1;
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
            return -1;
        }
    
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
    
        // ip和port进行转化
        inet_pton(AF_INET,ip,&servaddr.sin_addr);
        servaddr.sin_port = htons(port);
    
        if (bind(fd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1) {
            perror("bind error: ");
            return -1;
        }
    
        listen(fd,LISTENQ);
    
        return fd;
    }
    
    static int accept_client_proc(int srvfd)
    {
        struct sockaddr_in cliaddr;
        socklen_t cliaddrlen;
        cliaddrlen = sizeof(cliaddr);
        int clifd = -1;
    
        printf("accpet clint proc is called.\n");
    
    ACCEPT:
        clifd = accept(srvfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
    
        if (clifd == -1) {
            if (errno == EINTR) {
                goto ACCEPT;
            } else {
                fprintf(stderr, "accept fail,error:%s\n", strerror(errno));
                return -1;
            }
        }
    
        // ip和port进行转化
        fprintf(stdout, "accept a new client: %s:%d\n",
            inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
    
        //将新的连接描述符添加到数组中
        int i = 0;
        for (i = 0; i < SIZE; i++) {
            if (s_srv_ctx->clifds[i] < 0) {
                s_srv_ctx->clifds[i] = clifd;
                s_srv_ctx->cli_cnt++;
                break;
            }
        }
    
        if (i == SIZE) {
            fprintf(stderr,"too many clients.\n");
            return -1;
        }
    
    }
    
    static int handle_client_msg(int fd, char *buf) 
    {
        assert(buf);
        printf("recv buf is :%s\n", buf);
        write(fd, buf, strlen(buf) +1);
        return 0;
    }
    
    static void recv_client_msg(fd_set *readfds)
    {
        int i = 0, n = 0;
        int clifd;
        char buf[MAXLINE] = {0};
        for (i = 0;i <= s_srv_ctx->cli_cnt;i++) {
            clifd = s_srv_ctx->clifds[i];
            if (clifd < 0) {
                continue;
            }
            /*判断客户端套接字是否有数据*/
            if (FD_ISSET(clifd, readfds)) {
                //接收客户端发送的信息
                n = read(clifd, buf, MAXLINE);
                if (n <= 0) {
                    /*n==0表示读取完成,客户都关闭套接字*/
                    FD_CLR(clifd, &s_srv_ctx->allfds);
                    close(clifd);
                    s_srv_ctx->clifds[i] = -1;
                    continue;
                }
                handle_client_msg(clifd, buf);
            }
        }
    }
    static void handle_client_proc(int srvfd)
    {
        int  clifd = -1;
        int  retval = 0;
        fd_set *readfds = &s_srv_ctx->allfds;
        struct timeval tv;
        int i = 0;
    
        while (1) {
            /*每次调用select前都要重新设置文件描述符和时间,因为事件发生后,文件描述符和时间都被内核修改啦*/
            FD_ZERO(readfds);
            /*添加监听套接字*/
            FD_SET(srvfd, readfds);
            s_srv_ctx->maxfd = srvfd;
    
            tv.tv_sec = 30;
            tv.tv_usec = 0;
            /*添加客户端套接字*/
            for (i = 0; i < s_srv_ctx->cli_cnt; i++) {
                clifd = s_srv_ctx->clifds[i];
                /*去除无效的客户端句柄*/
                if (clifd != -1) {
                    FD_SET(clifd, readfds);
                }
                s_srv_ctx->maxfd = (clifd > s_srv_ctx->maxfd ? clifd : s_srv_ctx->maxfd);
            }
    
            /*开始轮询接收处理服务端和客户端套接字*/
            retval = select(s_srv_ctx->maxfd + 1, readfds, NULL, NULL, &tv);
            if (retval == -1) {
                fprintf(stderr, "select error:%s.\n", strerror(errno));
                return;
            }
            if (retval == 0) {
                fprintf(stdout, "select is timeout.\n");
                continue;
            }
            if (FD_ISSET(srvfd, readfds)) {
                /*监听客户端请求*/
                accept_client_proc(srvfd);
            } else {
                /*接受处理客户端消息*/
                recv_client_msg(readfds);
            }
        }
    }
    
    static void server_uninit()
    {
        if (s_srv_ctx) {
            free(s_srv_ctx);
            s_srv_ctx = NULL;
        }
    }
    
    static int server_init()
    {
        s_srv_ctx = (server_context_st *)malloc(sizeof(server_context_st));
        if (s_srv_ctx == NULL) {
            return -1;
        }
    
        memset(s_srv_ctx, 0, sizeof(server_context_st));
    
        int i = 0;
        for (;i < SIZE; i++) {
            s_srv_ctx->clifds[i] = -1;
        }
    
        return 0;
    }
    
    int main(int argc,char *argv[])
    {
        int  srvfd;
        /*初始化服务端context*/
        if (server_init() < 0) {
            return -1;
        }
        /*创建服务,开始监听客户端请求*/
        srvfd = create_server_proc(IPADDR, PORT);
        if (srvfd < 0) {
            fprintf(stderr, "socket create or bind fail.\n");
            goto err;
        }
        /*开始接收并处理客户端请求*/
        handle_client_proc(srvfd);
        server_uninit();
        return 0;
    err:
        server_uninit();
        return -1;
    }
2、client.cpp
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sys/select.h>
    #include <time.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    
    #define MAXLINE 1024
    #define IPADDRESS "127.0.0.1"
    #define SERV_PORT 8787
    
    #define max(a,b) (a > b) ? a : b
    
    static void handle_recv_msg(int sockfd, char *buf) 
    {
        printf("client recv msg is:%s\n", buf);
        sleep(5);
        write(sockfd, buf, strlen(buf) +1);
    }
    
    static void handle_connection(int sockfd)
    {
        char sendline[MAXLINE],recvline[MAXLINE];
        int maxfdp,stdineof;
        fd_set readfds;
        int n;
        struct timeval tv;
        int retval = 0;
    
        while (1) {
    
            FD_ZERO(&readfds);
            FD_SET(sockfd,&readfds);
            maxfdp = sockfd;
    
            tv.tv_sec = 5;
            tv.tv_usec = 0;
    
            retval = select(maxfdp+1,&readfds,NULL,NULL,&tv);
    
            if (retval == -1) {
                return ;
            }
    
            if (retval == 0) {
                printf("client timeout.\n");
                continue;
            }
    
            if (FD_ISSET(sockfd, &readfds)) {
                n = read(sockfd,recvline,MAXLINE);
                if (n <= 0) {
                    fprintf(stderr,"client: server is closed.\n");
                    close(sockfd);
                    FD_CLR(sockfd,&readfds);
                    return;
                }
    
                handle_recv_msg(sockfd, recvline);
            }
        }
    }
    
    int main(int argc,char *argv[])
    {
        int sockfd;
        struct sockaddr_in servaddr;
    
        sockfd = socket(AF_INET,SOCK_STREAM,0);
    
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(SERV_PORT);
        inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);
    
        int retval = 0;
        retval = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
        if (retval < 0) {
            fprintf(stderr, "connect fail,error:%s\n", strerror(errno));
            return -1;
        }
        
        sockaddr_in clientAddr;
        sockaddr_in serverAddr;
        socklen_t addrLen = sizeof(sockaddr_in);
    
        // 获取本地ip和port
        getsockname(sockfd, (struct sockaddr*)&clientAddr, &addrLen);
        // 获取对端ip和port
        getpeername(sockfd, (struct sockaddr*)&serverAddr, &addrLen);
    
        printf("client[%s:%d] connect server[%s:%d]\n",
            inet_ntoa(clientAddr.sin_addr),
            ntohs(clientAddr.sin_port),
            inet_ntoa(serverAddr.sin_addr),
            ntohs(serverAddr.sin_port));
    
        printf("client send to server.\n");
    
        char data[64] ={0};
        sprintf(data,"hello server[%s:%d],this is client[%s:%d]",
            inet_ntoa(serverAddr.sin_addr),
            ntohs(serverAddr.sin_port),
            inet_ntoa(clientAddr.sin_addr),
            ntohs(clientAddr.sin_port));
        write(sockfd, data, 64);
    
        handle_connection(sockfd);
    
        return 0;
    }
3、运行服务端
    [root@localhost Select]# g++ -o server server.cpp
    [root@localhost Select]# ./server
    accpet clint proc is called.
    accept a new client: 127.0.0.1:42108
    recv buf is :hello server[127.0.0.1:8787],this is client[127.0.0.1:42108]
    accpet clint proc is called.
    accept a new client: 127.0.0.1:42109
    recv buf is :hello server[127.0.0.1:8787],this is client[127.0.0.1:42109]
    recv buf is :hello server[127.0.0.1:8787],this is client[127.0.0.1:42108]
    recv buf is :hello server[127.0.0.1:8787],this is client[127.0.0.1:42109]
    recv buf is :hello server[127.0.0.1:8787],this is client[127.0.0.1:42108]
4、运行客户端
    [root@localhost Select]# g++ -o client client.cpp
    client[127.0.0.1:42108] connect server[127.0.0.1:8787]
    client send to server.
    client recv msg is:hello server[127.0.0.1:8787],this is client[127.0.0.1:42108]
    client recv msg is:hello server[127.0.0.1:8787],this is client[127.0.0.1:42108]
5、注意:if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) 这段代码的意义。
    没有这段代码,先关闭服务端,这时候连接处于 TIME_WAIT状态,在开启server报错:
    [root@localhost poll]# ./server
    bind error: : Address already in use
    加上上面的这段代码,就不会有这个问题。
IO多路复用__总结
1、IO多路复用解决什么问题?
    一个连接创建一个线程,用于这个连接的交互,但是线程的开销比较大,当连接很多的时候,就会出现严重的性能问题。
2、怎么解决这个问题?
    可以使用一个线程管理多个连接,一个连接对应一个socket句柄,在linux系统中就是文件描述符,这就是多路复用。
3、select特点
    a、监视的FD(文件描述符)个数有限,1024个
    b、对于FD集合,需要从用户态拷贝到内核态,同时进行线性扫描,存在性能问题
4、poll特点
    a、poll可以认为是加强版的select,可以监视的FD个数不限,因为他是基于链表的
    b、整体拷贝和线性扫描的问题,还是同样存在
5、epoll特点
    a、epoll解决了上述问题,可以监视的FD个数不限
    b、从用户态到内核态只拷贝一次
    c、select、poll不断主动轮询,把就绪的FD设置一下,在应用程序检查是否就绪。
        epoll也就是主动轮询,但是只把就绪的FD放入就绪链表中,把这个链表回调给应用程序。
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.