Socket-IO复用技术

发布时间:2020-07-19 22:08:06 作者:SherryX
来源:网络 阅读:2789

(上一篇地址)前面使用socket完成一个服务器对应多个客户端的小实验的时候,针对TCP连接,我们必须得创建新的进程来与新的客户端通信。那么,就意味着,1000个客户端就有有1000个server进程,这显然是不实际的。如果,我们可以提前把要监听的文件描述符放到一个集合里,一旦其中一个发生事件(不管是连上,还是通信),就去处理。这样,会方便很多。所以,今天学习一下IO复用。

1 五个I/O模型

select

         /* According to POSIX.1-2001 */
       #include <sys/select.h>

       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

       void FD_CLR(int fd, fd_set *set);//从集合中删除一个描述字
       int  FD_ISSET(int fd, fd_set *set);//描述字是否在该集合中
       void FD_SET(int fd, fd_set *set);//添加一个描述字到集合中
       void FD_ZERO(fd_set *set);//清空描述字集合

timeout参数

时间的结构体如下:

            struct timeval(
                long tv_sec;  //秒
                long tv_usec;//微秒
            );

timeout参数有三种可能

                #include <sys/epoll.h>
       int epoll_create(int size);//创建epoll
             int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);//操作函数
             int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);   

事件集合的结构体:
Socket-IO复用技术
(这里 ,还要注意,epoll的超时参数是int,单位是us)

使用流程

Socket-IO复用技术

        #include <sys/types.h>     
        #include <sys/socket.h>
        #include <netinet/in.h> //sockaddr_in
        #include <stdio.h>
        #include <string.h>
        #include <signal.h>
        #include <sys/epoll.h>

        //epoll
        //epoll_wait() epoll_creat() epoll_ctl()

        //TCP
        int main()
        {
            int fd;
            int clientfd;
            int ret;
            pid_t pid;

            int i;
            int epfd;
            int nEvent;
            struct epoll_event event = {0};
            struct epoll_event rtl_events[20] = {0};    //事件结果集

            int reuse = 0;
            char acbuf[20] = "";
            char client_addr[100] = "";
            struct sockaddr_in addr = {0};  //自己的地址
            struct sockaddr_in clientAddr = {0};    //连上的客户端的地址
            int addrLen = sizeof(struct sockaddr_in);

            signal(SIGCHLD,SIG_IGN);

            //1.socket()
            fd = socket(PF_INET,SOCK_STREAM,0);
            if(fd == -1)
            {
                perror("socket");
                return -1;
            }

            //会出现没有活动的套接字仍然存在,会禁止绑定端口,出现错误:address already in use .
            //由TCP套接字TIME_WAIT引起,bind 返回 EADDRINUSE,该状态会保留2-4分钟
            if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
                {
                perror("setsockopet error\n");
                return -1;
                }

            //2.bind()
            addr.sin_family = AF_INET;
            addr.sin_port = htons(1234);
            addr.sin_addr.s_addr = inet_addr("192.168.159.5");
            ret = bind(fd,(struct sockaddr *)&addr,addrLen);
            if(ret == -1)
            {
                perror("bind");
                return -1;
            }

            //3.listen()
            ret = listen(fd,10);
            if(ret == -1)
            {
                perror("listen");
                return -1;
            }

            epfd = epoll_create(1000);  //同时监听的文件描述符
            event.data.fd = fd;
            event.events = EPOLLIN;  //读
            epoll_ctl(epfd,EPOLL_CTL_ADD,fd, &event);
            while(1)
            {
        //      nEvent = epoll_wait(epfd,rtl_events,20,-1);  //-1:阻塞    0:非阻塞
                nEvent = epoll_wait(epfd,rtl_events,20,5000);
                if(nEvent == -1)
                {
                    perror("epoll_wait");
                    return -1;
                }
                else if(nEvent == 0)
                {
                    printf("time out.");
                }
                else
                {
                    //有事件发生,立即处理
                    for(i = 0; i < nEvent;i++)
                    {
                        //如果是 服务器fd
                        if( rtl_events[i].data.fd == fd )
                        {
                            clientfd = accept(fd,(struct sockaddr *)&clientAddr,&addrLen);
                            //添加
                            event.data.fd = clientfd;
                            event.events = EPOLLIN;  //读
                            epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&event);
                            printf("client ip:%s ,port:%u\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));
                        }
                        else
                        {
                            //否则 客户端fd 
                            memset(acbuf,0,20);
                            ret = read(rtl_events[i].data.fd,acbuf,20);
                            printf("%d\n",ret);
                            if( ret == 0) //客户端退出
                            {
                                close(rtl_events[i].data.fd);
                                //从集合里删除
                                epoll_ctl(epfd,EPOLL_CTL_DEL,rtl_events[i].data.fd,NULL);
                            }
                            else
                                printf("receive: %s\n",acbuf);
                        }

                    }
                }
            }

            return 0;
        }

运行结果如前,正常收发。

推荐阅读:
  1. 关于波分复用技术的CWDM与DWDM光模块介绍
  2. 多路复用controlfile文件

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

socket io 复用

上一篇:移植MonkeyRunner的图片对比和获取子图功能的实现-Appium篇

下一篇:记一次从oracle到hive的历史数据迁移(一)

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》