• epoll的实现与深入思考


        提契

        纸上得来终觉浅,绝知此事要躬行。

        正文

        前段时间写了一篇epoll的学习文章,但没有自己的心得总觉得比较肤浅,花了一些时间补充一个epoll的实例,并浅析一下过程中遇到的问题。

        上epoll_server的例子,epoll的代码都在这里

      1 #include<iostream>
      2 #include<stdlib.h>
      3 #include<sys/epoll.h>
      4 #include<sys/socket.h>
      5 #include<netinet/in.h>
      6 #include<sys/types.h>
      7 #include<fcntl.h>
      8 
      9 using namespace std;
     10 const int PORT = 8888;
     11 const int MAX_CLIENT_NUM = 10000;
     12 const int MAX_LEN = 2000;
     13 
     14 bool setfdnoblock(int fd)
     15 {
     16     int flg = fcntl(fd, F_GETFL);
     17     if(flg < 0)
     18     {
     19         cout << "get fd flag failed" << endl;
     20         return false;
     21     }
     22     if(fcntl(fd, F_SETFL, O_NONBLOCK | flg) < 0)
     23     {
     24         return false;
     25     }
     26     return true;
     27 }
     28 
     29 int CreateTcpServer(int port, int listennum)
     30 {
     31     int fd;
     32     fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     33 
     34     sockaddr_in TcpServer;
     35     bzero(&TcpServer, sizeof(TcpServer));
     36     TcpServer.sin_family = AF_INET;
     37     TcpServer.sin_port = htons(8888);
     38     TcpServer.sin_addr.s_addr = htonl(INADDR_ANY);
     39 
     40     int iRet = bind(fd, (struct sockaddr*)&TcpServer, sizeof(TcpServer));
     41     if(-1 == iRet)
     42     {
     43     cout << "server bind error!" << endl;
     44     return -1;
     45     }
     46     if(listen(fd, listennum) == -1)
     47     {
     48         cout << "server listen error" << endl;
     49         return -1;
     50     }
     51     return fd;
     52 }
     53 
     54 int main()
     55 {
     56     int Serverfd = CreateTcpServer(PORT, MAX_CLIENT_NUM);
     57     if(Serverfd == -1)
     58     {
     59         cout << "server create failed" << endl;
     60     }
     61     else
     62     {
     63        cout << "serverfd is :" << Serverfd << endl;
     64     }
     65 
     66     int Epollfd = epoll_create(MAX_CLIENT_NUM);
     67     if(Epollfd == -1)
     68     {
     69         cout << "epoll_create failed" << endl;
     70     }
     71     epoll_event ev, events[MAX_CLIENT_NUM];
     72     int nfds = 0;
     73     int client = 0;
     74     char buff[MAX_LEN];
     75     sockaddr_in CliAddr;
     76     unsigned int iCliSize = sizeof(CliAddr);
     77     ev.events = EPOLLIN|EPOLLOUT;
     78     ev.data.fd = Serverfd;
     79     if(!setfdnoblock(Serverfd))
     80     {
     81         cout << "set serverfd no_block failed" << endl;
     82     }
     83     if(epoll_ctl(Epollfd, EPOLL_CTL_ADD, Serverfd, &ev))
     84     {
     85         cout << "epoll add serverfd error" << endl;
     86     }
     87     while(1)
     88     {
     89         nfds = epoll_wait(Epollfd, events, MAX_CLIENT_NUM, 100000);
     90         if(nfds == -1)
     91         {
     92             cout << "error occur, exit" << endl;
     93             return -1;
     94         }
     95         else if( nfds == 0)
     96         {
     97             cout << "epoll_wait return zero" << endl;
     98         }
     99         else
    100         {
    101             for(int i = 0; i < nfds; i++)
    102             {
    103                 cout << "events[i].data.fd is :" << events[i].data.fd << endl;
    104                 if(events[i].data.fd == Serverfd)
    105                 {
    106                     cout << " Serverfd received event" << endl;
    107                     client = accept(Serverfd, (struct sockaddr*)&CliAddr, &iCliSize);
    108                     if(client == -1)
    109                     {
    110                         cout << "accept error" << endl;
    111                         return -1;
    112                     }
    113                     ev.data.fd = client;
    114                     if(!setfdnoblock(client))
    115                     {
    116                         cout << "set client fd no_block error" << endl;
    117                     }
    118                     if(epoll_ctl(Epollfd, EPOLL_CTL_ADD, client, &ev))
    119                     {
    120                         cout << "epoll add client error" << endl;
    121                     }
    122                     else
    123                     {
    124                         cout << "success add client" << endl;
    125                     }
    126                 }
    127                 else if(events[i].events&EPOLLIN)
    128                 {
    129                     cout << "recv client msg" << endl;
    130                     if(events[i].data.fd < 0)
    131                     {
    132                         cout << " event[i].data.fd is smaller than zero" << endl;
    133                         continue;
    134                     }
    135                     if(read(events[i].data.fd, buff, MAX_LEN) == -1)
    136                     {
    137                         perror("clifd read");
    138                     }
    139                     else
    140                     {
    141                         cout << "read client msg suc" << endl;
    142                         printf("%s",buff);
    143                     }
    144                     char resp[] = "recv a client msg, this is resp msg";
    145                     write(events[i].data.fd, resp, strlen(resp)+1);
    146                     //read and mod
    147                 }
    148                 else if(events[i].events&EPOLLOUT)
    149                 {
    150                     //send and mod
    151                 }
    152             }
    153         }
    154     }
    155 }

        下面是一个TCP的client例子

     1 #include<sys/types.h>
     2 #include<sys/socket.h>
     3 #include<netinet/in.h>
     4 #include<stdlib.h>
     5 #include<arpa/inet.h>
     6 #include<stdio.h>
     7 #include<string.h>
     8 #include<iostream>
     9 
    10 using namespace std;
    11 const int MAX_BUFF=2048;
    12 char notify[] = "i'm client";
    13 
    14 int main()
    15 {
    16     int Clifd;
    17     char buff[MAX_BUFF];
    18     Clifd = socket(AF_INET, SOCK_STREAM, 0);
    19     if(Clifd == -1)
    20     {
    21         perror("clifd socket");
    22     }
    23     sockaddr_in CliSock;
    24     bzero(&CliSock, sizeof(CliSock));
    25     CliSock.sin_family = AF_INET;
    26     CliSock.sin_addr.s_addr = inet_addr("203.195.243.17");
    27     CliSock.sin_port = htons(8888);
    28 
    29     if(-1 == connect(Clifd, (struct sockaddr*)&CliSock, sizeof(CliSock)))
    30     {
    31         perror("clifd connect");
    32     }
    33     if(write(Clifd, notify, strlen(notify)+1) == -1)
    34     {
    35         perror("clifd write");
    36     }
    37     cout << "write over" << endl;
    38     if(read(Clifd, buff, MAX_BUFF) == -1)
    39     {
    40         perror("clifd read");
    41     }
    42     else
    43     {
    44         cout << buff << endl;
    45     }
    46 }

        比较简单的代码,算是epoll的典型框架,不再解释代码,将编码过程犯得几个错拿出来供大家借鉴:

        1.epoll_ctl(Epollfd, EPOLL_CTL_ADD, Serverfd, &ev)添加Serverfd至epoll监视列表时ev.data.fd并没有设置等于serverfd,结果客户端来连接的时候不停收到EPOLLIN消息,但events[i].data.fd == Serverfd却从不成立,在epoll_ctl之前添加ev.data.fd = Serverfd解决问题,对此我的理解是epoll_ctl将第三参数Serverfd加入监视列表,并将ev结构体与Serverfd关联起来,但epoll不会主动监测和修改ev的其他参数,在epoll_wait时epoll监测到Serverfd上有可读的消息就返回,并将Serverfd关联的ev结构体赋值给events这个事件列表,所以events[i].data.fd其实就是epoll_ctl注册时的ev.data.fd。

        2.当client代码调用connect以后立即返回了连接建立成功的结果,此时epoll_server的accept函数还没有调用,那么TCP的连接建立原理是怎样的?

        从这张图片来看,TCP的server端在listen以后会被动接受client的请求并建立连接,accept和建立连接没有直接关系,accept只是从serverfd上获取到请求建连的client信息,accept之前已经完成了链路的创建。

        延伸话题

        同步/异步与阻塞/非阻塞

        同步/异步
        同步和异步往往和线程有关系,比如SendMsg,线程A->线程B,发送消息后需要等待其他线程或进程的响应。
        如果SendMsg以后线程A等待线程B返回响应消息,线程才继续处理,这就是同步
        如果SendMsg以后线程A就继续做自己的事情,而注册了一个回调或者响应线程来处理线程B的响应消息,这就是异步
        同步往往是阻塞的
        异步往往是非阻塞的

        阻塞/非阻塞
        阻塞read:线程将阻塞,直到有可读的数据。
        非阻塞read:设置O_NOBLOCK以后,如果fd没有可读的数据,read将立即返回-1并设errno为EAGAIN。
        在有可读数据时阻塞与非阻塞read是一样的。
        阻塞write:data从user-mode空间move到kernel-mode空间,之后系统完成kernel-mode到物理缓冲的处理并返回,然后阻塞IO返回。
        非阻塞write:data从user-mode空间move到kernel-mode空间,write返回。

        小结

        epoll的延伸应用、同步异步、阻塞非阻塞都可以谈很多,以后写对应的文章进行详解。

        补充

        腾讯云服务器真是一台裸机,ftp服务没有打开,默认只能ssh登陆root账户,man手册用法有点奇怪,用了一天以后还报了hostname错误和eth1找不到网络设备,感觉不是很成熟的产品。

  • 相关阅读:
    QML vs WEB
    《TO C产品经理进阶》
    《TO B产品设计标准化》
    《多元思维模型》跨学科及其核心思维模型
    产品经理审美训练
    Aria2多线程轻量级批量下载利器
    正则表达式
    如何开发一个用户脚本系列教程
    Aria2自动下载
    助贷
  • 原文地址:https://www.cnblogs.com/learn-my-life/p/3848208.html
Copyright © 2020-2023  润新知