zoukankan      html  css  js  c++  java
  • Linux高并发网络编程开发——广播-组播-本地套接字

    在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

    10-Linux系统编程-第14天(广播-组播-本地套接字)

    目录:
    一、学习目标
    二、复习
    三、广播
    1、广播通信流程
    2、广播服务器代码实现
    3、广播客户端代码实现
    四、组播
    1、组播通信流程
    2、组播服务器代码实现
    3、组播客户端代码实现
    五、本地套接字
    1、本地套接字通信流程
    2、本地套接字server实现
    3、本地套接字client实现
    4、心跳包
    六、epoll 反应堆模型

    一、学习目标

    1、广播(UDP)

    2、组播(UDP)

    3、本地套接字

    4、epoll 反应堆模型

    二、复习

    1、epoll

    2、udp通信流程

    3、tcp、udp使用场景

    三、广播

    1、广播通信流程

    2、广播服务器代码实现

    >mkdir broadcast

    >touch server.c

    >vi server.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <arpa/inet.h>
     8 
     9 int main(int argc, const char* argv[])
    10 {
    11     // 创建套接字
    12     int fd = socket(AF_INET, SOCK_DGRAM, 0);
    13     if(fd == -1)
    14     {
    15         perror("socket error");
    16         exit(1);
    17     }
    18 
    19     // 绑定server的iP和端口
    20     struct sockaddr_in serv;
    21     memset(&serv, 0, sizeof(serv));
    22     serv.sin_family  = AF_INET;
    23     serv.sin_port = htons(8787);    // server端口
    24     serv.sin_addr.s_addr = htonl(INADDR_ANY);
    25     int ret = bind(fd, (struct sockaddr*)&serv, sizeof(serv));
    26     if(ret == -1)
    27     {
    28         perror("bind error");
    29         exit(1);
    30     }
    31 
    32     // 初始化客户端地址信息
    33     struct sockaddr_in client;
    34     memset(&client, 0, sizeof(client));
    35     client.sin_family = AF_INET;
    36     client.sin_port = htons(6767);  // 客户端要绑定的端口
    37     // 使用广播地址给客户端发数据
    38     inet_pton(AF_INET, "192.168.123.255", &client.sin_addr.s_addr);
    39 
    40     // 给服务器开放广播权限
    41     int flag = 1;
    42     setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
    43 
    44     // 通信
    45     while(1)
    46     {
    47         // 一直给客户端发数据
    48         static int num = 0;
    49         char buf[1024] = {0};
    50         sprintf(buf, "hello, udp == %d
    ", num++);
    51         int ret = sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&client, sizeof(client));
    52         if(ret == -1)
    53         {
    54             perror("sendto error");
    55             break;
    56         }
    57         
    58         printf("server == send buf: %s
    ", buf);
    59 
    60         sleep(1);
    61     }
    62     
    63     close(fd);
    64 
    65     return 0;
    66 }

    >gcc server.c -o server

    3、广播客户端代码实现

    >touch client.c

    >vi client.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <arpa/inet.h>
     8 
     9 int main(int argc, const char* argv[])
    10 {
    11     int fd = socket(AF_INET, SOCK_DGRAM, 0);
    12     if(fd == -1)
    13     {
    14         perror("socket error");
    15         exit(1);
    16     }
    17 
    18     // 绑定iP和端口
    19     struct sockaddr_in client;
    20     memset(&client, 0, sizeof(client));
    21     client.sin_family = AF_INET;
    22     client.sin_port = htons(6767);  
    23     inet_pton(AF_INET, "0.0.0.0", &client.sin_addr.s_addr);
    24     int ret  = bind(fd, (struct sockaddr*)&client, sizeof(client));
    25     if(ret == -1)
    26     {
    27         perror("bind error");
    28         exit(1);
    29     }
    30 
    31     // 接收数据
    32     while(1)
    33     {
    34         char buf[1024] = {0};
    35         int len = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
    36         if(len == -1)
    37         {
    38             perror("recvfrom error");
    39             break;
    40         }
    41         
    42         printf("client == recv buf: %s
    ", buf);
    43     }
    44 
    45     close(fd);
    46     
    47     return 0;
    48 }

    >gcc client.c -o client

    >./server

    (打开另外一个终端,执行./client,查看这个终端的接收情况)

    四、组播

    1、组播通信流程

    》组播结构图:

    2、组播服务器代码实现

    >mkdir multicast

    >touch server.c

    >vi server.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <arpa/inet.h>
     8 #include <net/if.h>//名字转网卡编号:if_nametoindex
     9 
    10 int main(int argc, const char* argv[])
    11 {
    12     // 创建套接字
    13     int fd = socket(AF_INET, SOCK_DGRAM, 0);
    14     if(fd == -1)
    15     {
    16         perror("socket error");
    17         exit(1);
    18     }
    19 
    20     // 绑定server的iP和端口
    21     struct sockaddr_in serv;
    22     memset(&serv, 0, sizeof(serv));
    23     serv.sin_family  = AF_INET;
    24     serv.sin_port = htons(8787);    // server端口
    25     serv.sin_addr.s_addr = htonl(INADDR_ANY);
    26     int ret = bind(fd, (struct sockaddr*)&serv, sizeof(serv));
    27     if(ret == -1)
    28     {
    29         perror("bind error");
    30         exit(1);
    31     }
    32 
    33     // 初始化客户端地址信息
    34     struct sockaddr_in client;
    35     memset(&client, 0, sizeof(client));
    36     client.sin_family = AF_INET;
    37     client.sin_port = htons(6767);  // 客户端要绑定的端口
    38     // 使用组播地址给客户端发数据
    39     inet_pton(AF_INET, "239.0.0.10", &client.sin_addr.s_addr);
    40 
    41     // 给服务器开放组播权限
    42     struct ip_mreqn flag;
    43     // init flag
    44     inet_pton(AF_INET, "239.0.0.10", &flag.imr_multiaddr.s_addr);   // 组播地址
    45     inet_pton(AF_INET, "0.0.0.0", &flag.imr_address.s_addr);    // 本地IP
    46     flag.imr_ifindex = if_nametoindex("ens33");//需要查看自己本机的网卡名字:ifconfig
    47     setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &flag, sizeof(flag));
    48 
    49     // 通信
    50     while(1)
    51     {
    52         // 一直给客户端发数据
    53         static int num = 0;
    54         char buf[1024] = {0};
    55         sprintf(buf, "hello, udp == %d
    ", num++);
    56         int ret = sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&client, sizeof(client));
    57         if(ret == -1)
    58         {
    59             perror("sendto error");
    60             break;
    61         }
    62         
    63         printf("server == send buf: %s
    ", buf);
    64 
    65         sleep(1);
    66     }
    67     
    68     close(fd);
    69 
    70     return 0;
    71 }

    >gcc server.c -o server

    3、组播客户端代码实现

    >touch client.c

    >vi client.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <arpa/inet.h>
     8 #include <net/if.h>
     9 
    10 int main(int argc, const char* argv[])
    11 {
    12     int fd = socket(AF_INET, SOCK_DGRAM, 0);
    13     if(fd == -1)
    14     {
    15         perror("socket error");
    16         exit(1);
    17     }
    18 
    19     // 绑定iP和端口
    20     struct sockaddr_in client;
    21     memset(&client, 0, sizeof(client));
    22     client.sin_family = AF_INET;
    23     client.sin_port = htons(6767); // ........ 
    24     inet_pton(AF_INET, "0.0.0.0", &client.sin_addr.s_addr);
    25     int ret  = bind(fd, (struct sockaddr*)&client, sizeof(client));
    26     if(ret == -1)
    27     {
    28         perror("bind error");
    29         exit(1);
    30     }
    31 
    32     // 加入到组播地址
    33     struct ip_mreqn fl;
    34     inet_pton(AF_INET, "239.0.0.10", &fl.imr_multiaddr.s_addr);
    35     inet_pton(AF_INET, "0.0.0.0", &fl.imr_address.s_addr);
    36     fl.imr_ifindex = if_nametoindex("ens33");
    37     setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &fl, sizeof(fl));
    38 
    39     // 接收数据
    40     while(1)
    41     {
    42         char buf[1024] = {0};
    43         int len = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
    44         if(len == -1)
    45         {
    46             perror("recvfrom error");
    47             break;
    48         }
    49         
    50         printf("client == recv buf: %s
    ", buf);
    51     }
    52 
    53     close(fd);
    54     
    55     return 0;
    56 }

    >gcc client.c -o client

    >./server

    (打开另外一个终端,执行./client,查看这个终端的接收情况)

    五、本地套接字

    1、本地套接字通信流程

    》使用有名管道进行没有血缘关系的进程间通信

    》本地套接字:(使用本地套接字进行没有血缘关系的进程间通信

    》以TCP为例:

       

    》本地套接字进程通信:

    2、本地套接字server实现

    》以TCP为例:server需要创建两个套接字

    >mkdir localIPC

    >touch server.c

    >vi server.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <arpa/inet.h>
     8 #include <sys/un.h>//使用sockaddr_un结构体
     9 
    10 int main(int argc, const char* argv[])
    11 {
    12     int lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    13     if(lfd == -1)
    14     {
    15         perror("socket error");
    16         exit(1);
    17     }
    18 
    19     // 如果套接字文件存在, 删除套接字文件,防止端口复用!
    20     unlink("server.sock");
    21 
    22     // 绑定
    23     struct sockaddr_un serv;
    24     serv.sun_family = AF_LOCAL;
    25     strcpy(serv.sun_path, "server.sock");
    26     int ret = bind(lfd, (struct sockaddr*)&serv, sizeof(serv));
    27     if(ret == -1)
    28     {
    29         perror("bind error");
    30         exit(1);
    31     }
    32      
    33     // 监听
    34     ret = listen(lfd, 36);
    35     if(ret == -1)
    36     {
    37         perror("listen error");
    38         exit(1);
    39     }
    40 
    41     // 等待接收连接请求
    42     struct sockaddr_un client;
    43     socklen_t len = sizeof(client);
    44     int cfd = accept(lfd, (struct sockaddr*)&client, &len);
    45     if(cfd == -1)
    46     {
    47         perror("accept error");
    48         exit(1);
    49     }
    50     printf("======client bind file: %s
    ", client.sun_path);
    51      
    52     // 通信
    53     while(1)
    54     {
    55         char buf[1024] = {0};
    56         int recvlen = recv(cfd, buf, sizeof(buf), 0);
    57         if(recvlen == -1)
    58         {
    59             perror("recv error");
    60             exit(1);
    61         }
    62         else if(recvlen == 0)
    63         {
    64             printf("clietn disconnect ....
    ");
    65             close(cfd);
    66             break;
    67         }
    68         else
    69         {
    70             printf("recv buf: %s
    ", buf);
    71             send(cfd, buf, recvlen, 0);
    72         }
    73     }
    74     close(cfd);
    75     close(lfd);
    76     
    77     return 0;
    78 }

    >gcc server.c -o server

    3、本地套接字client实现

    》以TCP为例:client需要创建一个套接字

    >touch client.c

    >vi client.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <arpa/inet.h>
     8 #include <sys/un.h>
     9 
    10 int main(int argc, const char* argv[])
    11 {
    12     int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
    13     if(fd == -1)
    14     {
    15         perror("socket error");
    16         exit(1);
    17     }
    18   //防止端口复用!  
    19     unlink("client.sock");
    20 
    21     // ================================
    22     // 给客户端绑定一个套接字文件
    23     struct sockaddr_un client;
    24     client.sun_family = AF_LOCAL;
    25     strcpy(client.sun_path, "client.sock");
    26     int ret = bind(fd, (struct sockaddr*)&client, sizeof(client));
    27     if(ret == -1)
    28     {
    29         perror("bind error");
    30         exit(1);
    31     }
    32 
    33     // 初始化server信息
    34     struct sockaddr_un serv;
    35     serv.sun_family = AF_LOCAL;
    36     strcpy(serv.sun_path, "server.sock");
    37 
    38     // 连接服务器
    39     connect(fd, (struct sockaddr*)&serv, sizeof(serv));
    40 
    41     // 通信
    42     while(1)
    43     {
    44         char buf[1024] = {0};
    45         fgets(buf, sizeof(buf), stdin);//从终端输入数据
    46         send(fd, buf, strlen(buf)+1, 0);
    47 
    48         // 接收数据
    49         recv(fd, buf, sizeof(buf), 0);
    50         printf("recv buf: %s
    ", buf);
    51     }
    52 
    53     close(fd);
    54 
    55     return 0;
    56 }
    57     

    >gcc client.c -o client

    >./server

    (打开另外一个终端,执行./client,查看这个终端的接收情况)

    》问题描述:Ctrl+c结束终端,再次启用,报错:端口已被使用?

    为了防止端口复用,需要 rm server.sock和rm client.sock,但是为了更好地解决这个问题,使用unlink

    4、心跳包

    六、epoll 反应堆模型

    》分析程序(epoll_loop.c):

      1 /*
      2  * epoll基于非阻塞I/O事件驱动
      3  */
      4 #include <stdio.h>
      5 #include <sys/socket.h>
      6 #include <sys/epoll.h>
      7 #include <arpa/inet.h>
      8 #include <fcntl.h>
      9 #include <unistd.h>
     10 #include <errno.h>
     11 #include <string.h>
     12 #include <stdlib.h>
     13 #include <time.h>
     14 
     15 #define MAX_EVENTS  1024                                    //监听上限数
     16 #define BUFLEN      4096
     17 #define SERV_PORT   8080
     18 
     19 void recvdata(int fd, int events, void *arg);
     20 void senddata(int fd, int events, void *arg);
     21 
     22 /* 描述就绪文件描述符相关信息 */
     23 
     24 struct myevent_s {
     25     int fd;                                                 //要监听的文件描述符
     26     int events;                                             //对应的监听事件
     27     void *arg;                                              //泛型参数
     28     void (*call_back)(int fd, int events, void *arg);       //回调函数
     29     int status;                                             //是否在监听:1->在红黑树上(监听), 0->不在(不监听)
     30     char buf[BUFLEN];
     31     int len;
     32     long last_active;                                       //记录每次加入红黑树 g_efd 的时间值
     33 };
     34 
     35 int g_efd;                                                  //全局变量, 保存epoll_create返回的文件描述符
     36 struct myevent_s g_events[MAX_EVENTS+1];                    //自定义结构体类型数组. +1-->listen fd
     37 
     38 
     39 /*将结构体 myevent_s 成员变量 初始化*/
     40 
     41 void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg)
     42 {
     43     ev->fd = fd;
     44     ev->call_back = call_back;
     45     ev->events = 0;
     46     ev->arg = arg;
     47     ev->status = 0;
     48     //memset(ev->buf, 0, sizeof(ev->buf));
     49     //ev->len = 0;
     50     ev->last_active = time(NULL);    //调用eventset函数的时间
     51 
     52     return;
     53 }
     54 
     55 /* 向 epoll监听的红黑树 添加一个 文件描述符 */
     56 
     57 void eventadd(int efd, int events, struct myevent_s *ev)
     58 {
     59     struct epoll_event epv = {0, {0}};
     60     int op;
     61     epv.data.ptr = ev;
     62     epv.events = ev->events = events;       //EPOLLIN 或 EPOLLOUT
     63 
     64     if (ev->status == 1) {                                          //已经在红黑树 g_efd 里
     65         op = EPOLL_CTL_MOD;                                         //修改其属性
     66     } else {                                //不在红黑树里
     67         op = EPOLL_CTL_ADD;                 //将其加入红黑树 g_efd, 并将status置1
     68         ev->status = 1;
     69     }
     70 
     71     if (epoll_ctl(efd, op, ev->fd, &epv) < 0)                       //实际添加/修改
     72         printf("event add failed [fd=%d], events[%d]
    ", ev->fd, events);
     73     else
     74         printf("event add OK [fd=%d], op=%d, events[%0X]
    ", ev->fd, op, events);
     75 
     76     return ;
     77 }
     78 
     79 /* 从epoll 监听的 红黑树中删除一个 文件描述符*/
     80 
     81 void eventdel(int efd, struct myevent_s *ev)
     82 {
     83     struct epoll_event epv = {0, {0}};
     84 
     85     if (ev->status != 1)                                        //不在红黑树上
     86         return ;
     87 
     88     epv.data.ptr = ev;
     89     ev->status = 0;                                             //修改状态
     90     epoll_ctl(efd, EPOLL_CTL_DEL, ev->fd, &epv);                //从红黑树 efd 上将 ev->fd 摘除
     91 
     92     return ;
     93 }
     94 
     95 /*  当有文件描述符就绪, epoll返回, 调用该函数 与客户端建立链接 */
     96 // 回调函数 - 监听的文件描述符发送读事件时被调用
     97 void acceptconn(int lfd, int events, void *arg)
     98 {
     99     struct sockaddr_in cin;
    100     socklen_t len = sizeof(cin);
    101     int cfd, i;
    102 
    103     if ((cfd = accept(lfd, (struct sockaddr *)&cin, &len)) == -1) {
    104         if (errno != EAGAIN && errno != EINTR) {
    105             /* 暂时不做出错处理 */
    106         }
    107         printf("%s: accept, %s
    ", __func__, strerror(errno));
    108         return ;
    109     }
    110 
    111     do {
    112         for (i = 0; i < MAX_EVENTS; i++)                                //从全局数组g_events中找一个空闲元素
    113             if (g_events[i].status == 0)                                //类似于select中找值为-1的元素
    114                 break;                                                  //跳出 for
    115 
    116         if (i == MAX_EVENTS) {
    117             printf("%s: max connect limit[%d]
    ", __func__, MAX_EVENTS);
    118             break;                                                      //跳出do while(0) 不执行后续代码
    119         }
    120 
    121         int flag = 0;
    122         if ((flag = fcntl(cfd, F_SETFL, O_NONBLOCK)) < 0) {             //将cfd也设置为非阻塞
    123             printf("%s: fcntl nonblocking failed, %s
    ", __func__, strerror(errno));
    124             break;
    125         }
    126 
    127         /* 给cfd设置一个 myevent_s 结构体, 回调函数 设置为 recvdata */
    128 
    129         eventset(&g_events[i], cfd, recvdata, &g_events[i]);   
    130         eventadd(g_efd, EPOLLIN, &g_events[i]);                         //将cfd添加到红黑树g_efd中,监听读事件
    131 
    132     } while(0);
    133 
    134     printf("new connect [%s:%d][time:%ld], pos[%d]
    ", 
    135             inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), g_events[i].last_active, i);
    136     return ;
    137 }
    138 
    139 // 回调函数 - 通信的文件描述符发生读事件时候被调用
    140 void recvdata(int fd, int events, void *arg)
    141 {
    142     struct myevent_s *ev = (struct myevent_s *)arg;
    143     int len;
    144 
    145     len = recv(fd, ev->buf, sizeof(ev->buf), 0);            //读文件描述符, 数据存入myevent_s成员buf中
    146 
    147     eventdel(g_efd, ev);        //将该节点从红黑树上摘除
    148 
    149     if (len > 0) {
    150 
    151         ev->len = len;
    152         ev->buf[len] = '';                                //手动添加字符串结束标记
    153         printf("C[%d]:%s
    ", fd, ev->buf);
    154 
    155         eventset(ev, fd, senddata, ev);                     //设置该 fd 对应的回调函数为 senddata
    156         eventadd(g_efd, EPOLLOUT, ev);                      //将fd加入红黑树g_efd中,监听其写事件
    157 
    158     } else if (len == 0) {
    159         close(ev->fd);
    160         /* ev-g_events 地址相减得到偏移元素位置 */
    161         printf("[fd=%d] pos[%ld], closed
    ", fd, ev-g_events);
    162     } else {
    163         close(ev->fd);
    164         printf("recv[fd=%d] error[%d]:%s
    ", fd, errno, strerror(errno));
    165     }
    166 
    167     return;
    168 }
    169 
    170 // 回调函数 - 通信的文件描述符发生写事件时候被调用
    171 void senddata(int fd, int events, void *arg)
    172 {
    173     struct myevent_s *ev = (struct myevent_s *)arg;
    174     int len;
    175 
    176     len = send(fd, ev->buf, ev->len, 0);                    //直接将数据 回写给客户端。未作处理
    177     /*
    178     printf("fd=%d	ev->buf=%s	tev->len=%d
    ", fd, ev->buf, ev->len);
    179     printf("send len = %d
    ", len);
    180     */
    181 
    182     if (len > 0) {
    183 
    184         printf("send[fd=%d], [%d]%s
    ", fd, len, ev->buf);
    185         eventdel(g_efd, ev);                                //从红黑树g_efd中移除
    186         eventset(ev, fd, recvdata, ev);                     //将该fd的 回调函数改为 recvdata
    187         eventadd(g_efd, EPOLLIN, ev);                       //从新添加到红黑树上, 设为监听读事件
    188 
    189     } else {
    190         close(ev->fd);                                      //关闭链接
    191         eventdel(g_efd, ev);                                //从红黑树g_efd中移除
    192         printf("send[fd=%d] error %s
    ", fd, strerror(errno));
    193     }
    194 
    195     return ;
    196 }
    197 
    198 /*创建 socket, 初始化lfd */
    199 
    200 void initlistensocket(int efd, short port)
    201 {
    202     int lfd = socket(AF_INET, SOCK_STREAM, 0);
    203     fcntl(lfd, F_SETFL, O_NONBLOCK);                                            //将socket设为非阻塞
    204 
    205     /* void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg);  */
    206     eventset(&g_events[MAX_EVENTS], lfd, acceptconn, &g_events[MAX_EVENTS]);
    207 
    208     /* void eventadd(int efd, int events, struct myevent_s *ev) */
    209     eventadd(efd, EPOLLIN, &g_events[MAX_EVENTS]);
    210 
    211     struct sockaddr_in sin;
    212     memset(&sin, 0, sizeof(sin));                                               //bzero(&sin, sizeof(sin))
    213     sin.sin_family = AF_INET;
    214     sin.sin_addr.s_addr = INADDR_ANY;
    215     sin.sin_port = htons(port);
    216 
    217     bind(lfd, (struct sockaddr *)&sin, sizeof(sin));
    218 
    219     listen(lfd, 20);
    220 
    221     return ;
    222 }
    223 
    224 int main(int argc, char *argv[])
    225 {
    226     unsigned short port = SERV_PORT;
    227 
    228     if (argc == 2)
    229         port = atoi(argv[1]);                           //使用用户指定端口.如未指定,用默认端口
    230 
    231     g_efd = epoll_create(MAX_EVENTS+1);                 //创建红黑树,返回给全局 g_efd 
    232     if (g_efd <= 0)
    233         printf("create efd in %s err %s
    ", __func__, strerror(errno));
    234 
    235     initlistensocket(g_efd, port);                      //初始化监听socket
    236 
    237     struct epoll_event events[MAX_EVENTS+1];            //保存已经满足就绪事件的文件描述符数组 
    238     printf("server running:port[%d]
    ", port);
    239 
    240     int checkpos = 0, i;
    241     while (1) {
    242         /* 超时验证,每次测试100个链接,不测试listenfd 当客户端60秒内没有和服务器通信,则关闭此客户端链接 */
    243 
    244         long now = time(NULL);                          //当前时间
    245         for (i = 0; i < 100; i++, checkpos++) {         //一次循环检测100个。 使用checkpos控制检测对象
    246             if (checkpos == MAX_EVENTS)
    247                 checkpos = 0;
    248             if (g_events[checkpos].status != 1)         //不在红黑树 g_efd 上
    249                 continue;
    250 
    251             long duration = now - g_events[checkpos].last_active;       //客户端不活跃的世间
    252 
    253             if (duration >= 60) {
    254                 close(g_events[checkpos].fd);                           //关闭与该客户端链接
    255                 printf("[fd=%d] timeout
    ", g_events[checkpos].fd);
    256                 eventdel(g_efd, &g_events[checkpos]);                   //将该客户端 从红黑树 g_efd移除
    257             }
    258         }
    259 
    260         /*监听红黑树g_efd, 将满足的事件的文件描述符加至events数组中, 1秒没有事件满足, 返回 0*/
    261         int nfd = epoll_wait(g_efd, events, MAX_EVENTS+1, 1000);
    262         if (nfd < 0) {
    263             printf("epoll_wait error, exit
    ");
    264             break;
    265         }
    266 
    267         for (i = 0; i < nfd; i++) {
    268             /*使用自定义结构体myevent_s类型指针, 接收 联合体data的void *ptr成员*/
    269             struct myevent_s *ev = (struct myevent_s *)events[i].data.ptr;  
    270 
    271             if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {           //读就绪事件
    272                 ev->call_back(ev->fd, events[i].events, ev->arg);
    273             }
    274             if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {         //写就绪事件
    275                 ev->call_back(ev->fd, events[i].events, ev->arg);
    276             }
    277         }
    278     }
    279 
    280     /* 退出前释放所有资源 */
    281     return 0;
    282 }

    》分析了

    (1)epoll反应堆模型main函数

    (2)epoll反应堆模型监听套接字初始函数:initlistensocket函数、eventset函数、eventset函数

    (3)epoll反应堆模型acceptconn函数

    在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

  • 相关阅读:
    EL表达式(Expression Language)
    JSP简单功能介绍
    MySQL基础使用
    JDBC
    MySQL安装卸载
    stanfordnlp dependencies(依存关系简写表)
    不需要深度学习就能做的文本分类器
    词向量的cbow模型
    pytorch实现自己的textCNN
    OpenCV编译viz模块
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_Linux_14.html
Copyright © 2011-2022 走看看