• Linux高并发网络编程开发——epoll-udp


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

    10-Linux系统编程-第13天(epoll-udp)

    目录:
    一、学习目标
    二、复习
    1、通过gdb定位段错误的位置
    2、TCP状态转换复习
    三、epoll
    1、epoll相关的函数介绍和工作流程
    2、epoll模型伪代码
    3、epoll模型代码实现
    4、epoll三种工作模式
    5、测试—epoll水平触发模式
    6、测试—边沿触发模式
    7、测试—边沿非阻塞模式
    8、文件描述符突破1024
    四、UDP
    1、UDP通信流程
    2、UDP服务器端代码实现
    3、UDP客户端代码实现

    一、学习目标

    1、了解poll操作函数

    2、熟练使用epoll多路IO模型

    3、了解epoll ET/LT 触发模式

    4、说出UDP的通信流程

    二、复习

    1、通过gdb定位段错误的位置

    测试(多线程server端程序:pthread_server.c):先把文件名改简单,如:test.c

      1 #include <stdio.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <sys/types.h>
      5 #include <string.h>
      6 #include <sys/socket.h>
      7 #include <arpa/inet.h>
      8 #include <ctype.h>
      9 #include <pthread.h>
     10 
     11 //自定义数据结构
     12 typedef struct SockInfo
     13 {
     14     int fd;//文件描述符
     15     struct sockaddr_in addr;//存放ip地址的结构体
     16     pthread_t id;//线程id
     17 }SockInfo;
     18 
     19 //子线程处理函数
     20 void *worker(void *arg)
     21 {
     22     char ip[64];
     23     char buf[1024];
     24     SockInfo *info = (SockInfo *)arg;
     25     //通信
     26     while(1)
     27     {
     28         printf("Client IP: %s, port: %d
    ", inet_ntop(AF_INET, &info->addr.sin_addr.s_addr, ip, sizeof(ip)),ntohs(info->addr.sin_port));
     29         int len = read(info->fd, buf, sizeof(buf));
     30         if(len == -1)
     31         {
     32             perror("read error");
     33             pthread_exit(NULL);
     34         }
     35         else if(len == 0)
     36         {
     37             printf("客户端已经断开了连接
    ");
     38             close(info->fd);
     39             break;
     40         }
     41         else
     42         {
     43             printf("recv buf: %s
    ", buf);
     44             write(info->fd, buf, len);
     45         }
     46     }
     47     return NULL;
     48 }
     49 
     50 //主函数
     51 int main(int argc, const char *argv[])
     52 {
     53     if(argc < 2)
     54     {
     55         printf("eg: ./a.out port
    ");
     56         exit(1);
     57     }
     58     struct sockaddr_in serv_addr;
     59     socklen_t serv_len = sizeof(serv_addr);
     60     int port = atoi(argv[1]);
     61     
     62     //创建套接字
     63     int lfd = socket(AF_INET, SOCK_STREAM, 0);
     64     //初始化服务器 sockaddr_in
     65     memset(&serv_addr, 0, serv_len);
     66     serv_addr.sin_family = AF_INET;//地址族
     67     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本机所有IP
     68     serv_addr.sin_port = htons(port);//设置端口
     69     //绑定IP 和端口
     70     bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
     71     
     72     //设置同时监听的最大个数
     73     listen(lfd, 36);
     74     printf("Start accept ......
    ");
     75     
     76     int i = 0;
     77     SockInfo info[256];
     78     //规定 fd == -1
     79     for(i = 0; i < sizeof(info) / sizeof(info[0]); ++i)
     80     {
     81         info[i].fd = -1;
     82     }
     83     
     84     socklen_t cli_len = sizeof(struct sockaddr_in);
     85     while(1)
     86     {    
     87         //选一个没有被使用的,最小的数组元素
     88         for(i = 0; i < 256; ++i)
     89         {
     90             if(info[i].fd == -1)
     91             {
     92                 break;
     93             }
     94         }
     95         if(i == 256)
     96         {
     97             break;
     98         }
     99         //主线程 - 等待接收连接请求
    100         info[i].fd = accept(lfd, (struct sockaddr*)&info[i].addr, &cli_len);
    101         
    102         //创建子线程 - 通信
    103         pthrad_create(&info[i].i, NULL, worker, &info[i]);
    104         //设置线程分离
    105         pthread_detach(info[i].fd);
    106         
    107     }
    108     
    109     close(lfd);
    110     
    111     //只退出主线程
    112     pthread_exit(NULL);
    113     return 0;
    114 }

    (问题描述:运行服务器端程序,然后打开另一个终端,运行客户端程序,原服务器终端报错:段错误)

    >gcc  test.c -g -lpthread

    >gdb ./a.out

    (gdb)>set args 9898

    (gdb)>r

    (打开另一个终端,运行./client 127.0.0.1 9898,连接server端)

    >quit

    (从错误信息看出pthread_detach出错,在源代码中找pthread_detach,将pthread_detach(info[i].fd);中fd改为id问题解决。)

    》段错误出现的原因分析:操作了非法的内存:访问了不该访问的内存;访问了这块内存,没有写权限,强制写操作;操作了本不属于该访问的内存;操作了内存中的内核区;操作了不存在的内存...

    》fd是文件描述符表中的——>文件描述符表在PCB中——>PCB在内核中——>作为普通用户,无法修改内核中的数据——>所以,出现了段错误

    2、TCP状态转换复习

    三、epoll

    1、epoll相关的函数介绍和工作流程

    2、epoll模型伪代码

    》epoll模型伪代码:

     1 int main()
     2 {
     3     //创建监听的套接字
     4     int lfd = socket();
     5     //绑定
     6     bind();
     7     //监听
     8     listen();
     9     
    10     //epoll树根节点
    11     int epfd = epoll_create(3000);
    12     //存储发送变化的fd对应信息
    13     struct epoll_event all[3000];
    14     //init
    15     //监听的lfd挂到epoll树上
    16     struct epoll_event ev;
    17     //在ev中init lfd信息
    18     ev.events = EPOLLIN;
    19     ev.data.fd = lfd;
    20     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
    21     while(1)
    22     {
    23         //委托内核检测事件
    24         int ret = epoll_wait(epfd, all, 3000, -1);
    25         //根据ret遍历all数组
    26         for(int i = 0; i < ret; ++i)
    27         {
    28             int fd = all[i].data.fd;
    29             //有新的连接
    30             if(fd == lfd)
    31             {
    32                 //接收连接请求-accept不阻塞
    33                 int cfd = accept();
    34                 //cfd上树
    35                 ev.events = EPOLLIN;
    36                 ev.data.fd = cfd;
    37                 epoll_ctl(epfd, epoll_ctl_add, cfd, &ev);
    38             }
    39             //已经连接的客户端有数据发送过来
    40             else
    41             {
    42                 //只处理客户端发来的数据
    43                 if(!all[i].events & EPOLLIN)
    44                 {
    45                     continue;
    46                 }
    47                 //读数据
    48                 int len = recv();
    49                 if(len == 0)
    50                 {
    51                     //检测的fd从树上删除
    52                     epoll_ctl(epfd, epoll_ctl_del, fd, NULL);
    53                     close(fd);
    54                 }
    55                 //写数据
    56                 send();
    57             }
    58         }
    59     }
    60     
    61 }

    3、epoll模型代码实现

    >touch epoll.c

    >vi epoll.c

      1 #include <stdio.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <sys/types.h>
      5 #include <string.h>
      6 #include <sys/socket.h>
      7 #include <arpa/inet.h>
      8 #include <ctype.h>
      9 #include <sys/epoll.h>
     10 
     11 
     12 int main(int argc, const char* argv[])
     13 {
     14     if(argc < 2)
     15     {
     16         printf("eg: ./a.out port
    ");
     17         exit(1);
     18     }
     19     struct sockaddr_in serv_addr;
     20     socklen_t serv_len = sizeof(serv_addr);
     21     int port = atoi(argv[1]);//字符串转整型
     22 
     23     // 创建套接字
     24     int lfd = socket(AF_INET, SOCK_STREAM, 0);
     25     // 初始化服务器 sockaddr_in 
     26     memset(&serv_addr, 0, serv_len);
     27     serv_addr.sin_family = AF_INET;                   // 地址族 
     28     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);    // 监听本机所有的IP,有人不写htonl,因为对于0来说,大端和小端是一样的
     29     serv_addr.sin_port = htons(port);            // 设置端口(htons小端转大端)
     30     // 绑定IP和端口
     31     bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
     32 
     33     // 设置同时监听的最大个数
     34     listen(lfd, 36);
     35     printf("Start accept ......
    ");
     36 
     37     struct sockaddr_in client_addr;
     38     socklen_t cli_len = sizeof(client_addr);
     39 
     40     // 创建epoll树根节点
     41     int epfd = epoll_create(2000);
     42     // 初始化epoll树
     43     struct epoll_event ev;
     44     ev.events = EPOLLIN;
     45     ev.data.fd = lfd;
     46     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
     47 
     48     struct epoll_event all[2000];
     49     while(1)
     50     {
     51         // 使用epoll通知内核fd 文件IO检测
     52         int ret = epoll_wait(epfd, all, sizeof(all)/sizeof(all[0]), -1);
     53 
     54         // 遍历all数组中的前ret个元素
     55         for(int i=0; i<ret; ++i)
     56         {
     57             int fd = all[i].data.fd;
     58             // 判断是否有新连接
     59             if(fd == lfd)
     60             {
     61                 // 接受连接请求
     62                 int cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len);
     63                 if(cfd == -1)
     64                 {
     65                     perror("accept error");
     66                     exit(1);
     67                 }
     68                 // 将新得到的cfd挂到树上
     69                 struct epoll_event temp;
     70                 temp.events = EPOLLIN;
     71                 temp.data.fd = cfd;
     72                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
     73                 
     74                 // 打印客户端信息
     75                 char ip[64] = {0};
     76                 printf("New Client IP: %s, Port: %d
    ",
     77                     inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)),
     78                     ntohs(client_addr.sin_port));
     79                 
     80             }
     81             else
     82             {
     83                 // 处理已经连接的客户端发送过来的数据
     84                 if(!all[i].events & EPOLLIN) 
     85                 {
     86                     continue;
     87                 }
     88 
     89                 // 读数据
     90                 char buf[1024] = {0};
     91                 int len = recv(fd, buf, sizeof(buf), 0);
     92                 if(len == -1)
     93                 {
     94                     perror("recv error");
     95                     exit(1);
     96                 }
     97                 else if(len == 0)
     98                 {
     99                     printf("client disconnected ....
    ");
    100                     // fd从epoll树上删除
    101                     ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
    102                     if(ret == -1)
    103                     {
    104                         perror("epoll_ctl - del error");
    105                         exit(1);
    106                     }
    107                     close(fd);
    108                     
    109                 }
    110                 else
    111                 {
    112                     printf(" recv buf: %s
    ", buf);
    113                     write(fd, buf, len);
    114                 }
    115             }
    116         }
    117     }
    118 
    119     close(lfd);
    120     return 0;
    121 }

    >gcc epoll.c

    >./a.out 9876

    (打开另外两个终端,执行./client  9876,然后分别输入数据,看原server终端的接收情况)

    4、epoll三种工作模式

    5、测试—epoll水平触发模式

    >cp epoll.c lt_epoll.c,然后更改:

    >vi lt_epoll.c

      1 #include <stdio.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <sys/types.h>
      5 #include <string.h>
      6 #include <sys/socket.h>
      7 #include <arpa/inet.h>
      8 #include <ctype.h>
      9 #include <sys/epoll.h>
     10 
     11 
     12 int main(int argc, const char* argv[])
     13 {
     14     if(argc < 2)
     15     {
     16         printf("eg: ./a.out port
    ");
     17         exit(1);
     18     }
     19     struct sockaddr_in serv_addr;
     20     socklen_t serv_len = sizeof(serv_addr);
     21     int port = atoi(argv[1]);
     22 
     23     // 创建套接字
     24     int lfd = socket(AF_INET, SOCK_STREAM, 0);
     25     // 初始化服务器 sockaddr_in 
     26     memset(&serv_addr, 0, serv_len);
     27     serv_addr.sin_family = AF_INET;                   // 地址族 
     28     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);    // 监听本机所有的IP
     29     serv_addr.sin_port = htons(port);            // 设置端口 
     30     // 绑定IP和端口
     31     bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
     32 
     33     // 设置同时监听的最大个数
     34     listen(lfd, 36);
     35     printf("Start accept ......
    ");
     36 
     37     struct sockaddr_in client_addr;
     38     socklen_t cli_len = sizeof(client_addr);
     39 
     40     // 创建epoll树根节点
     41     int epfd = epoll_create(2000);
     42     // 初始化epoll树
     43     struct epoll_event ev;
     44     ev.events = EPOLLIN;
     45     ev.data.fd = lfd;
     46     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
     47 
     48     struct epoll_event all[2000];
     49     while(1)
     50     {
     51         // 使用epoll通知内核fd 文件IO检测
     52         int ret = epoll_wait(epfd, all, sizeof(all)/sizeof(all[0]), -1);
     53         printf("================== epoll_wait =============
    ");
     54 
     55         // 遍历all数组中的前ret个元素
     56         for(int i=0; i<ret; ++i)
     57         {
     58             int fd = all[i].data.fd;
     59             // 判断是否有新连接
     60             if(fd == lfd)
     61             {
     62                 // 接受连接请求
     63                 int cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len);
     64                 if(cfd == -1)
     65                 {
     66                     perror("accept error");
     67                     exit(1);
     68                 }
     69                 // 将新得到的cfd挂到树上
     70                 struct epoll_event temp;
     71                 temp.events = EPOLLIN;
     72                 temp.data.fd = cfd;
     73                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
     74                 
     75                 // 打印客户端信息
     76                 char ip[64] = {0};
     77                 printf("New Client IP: %s, Port: %d
    ",
     78                     inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)),
     79                     ntohs(client_addr.sin_port));
     80                 
     81             }
     82             else
     83             {
     84                 // 处理已经连接的客户端发送过来的数据
     85                 if(!all[i].events & EPOLLIN) 
     86                 {
     87                     continue;
     88                 }
     89 
     90                 // 读数据
     91                 char buf[5] = {0};
     92                 int len = recv(fd, buf, sizeof(buf), 0);
     93                 if(len == -1)
     94                 {
     95                     perror("recv error");
     96                     exit(1);
     97                 }
     98                 else if(len == 0)
     99                 {
    100                     printf("client disconnected ....
    ");
    101                     // fd从epoll树上删除
    102                     ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
    103                     if(ret == -1)
    104                     {
    105                         perror("epoll_ctl - del error");
    106                         exit(1);
    107                     }
    108                     close(fd);
    109                     
    110                 }
    111                 else
    112                 {  //printf标准析构函数,缓冲区大小8k,只有满了才会输出,除非遇到“
    ”
    113                     // printf(" recv buf: %s
    ", buf);//printf打印会出错
    114                     write(STDOUT_FILENO, buf, len);
    115                     write(fd, buf, len);
    116                 }
    117             }
    118         }
    119     }
    120 
    121     close(lfd);
    122     return 0;
    123 }

    >gcc lt_epoll.c -o lt

    >./lt 9876

    (打开另外两个终端,执行./client  9876,然后分别输入数据,看原server终端的接收情况)

    6、测试—边沿触发模式

    >cp lt_epoll.c et_epoll.c,然后更改:

    >vi et_epoll.c

      1 #include <stdio.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <sys/types.h>
      5 #include <string.h>
      6 #include <sys/socket.h>
      7 #include <arpa/inet.h>
      8 #include <ctype.h>
      9 #include <sys/epoll.h>
     10 
     11 
     12 int main(int argc, const char* argv[])
     13 {
     14     if(argc < 2)
     15     {
     16         printf("eg: ./a.out port
    ");
     17         exit(1);
     18     }
     19     struct sockaddr_in serv_addr;
     20     socklen_t serv_len = sizeof(serv_addr);
     21     int port = atoi(argv[1]);
     22 
     23     // 创建套接字
     24     int lfd = socket(AF_INET, SOCK_STREAM, 0);
     25     // 初始化服务器 sockaddr_in 
     26     memset(&serv_addr, 0, serv_len);
     27     serv_addr.sin_family = AF_INET;                   // 地址族 
     28     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);    // 监听本机所有的IP
     29     serv_addr.sin_port = htons(port);            // 设置端口 
     30     // 绑定IP和端口
     31     bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
     32 
     33     // 设置同时监听的最大个数
     34     listen(lfd, 36);
     35     printf("Start accept ......
    ");
     36 
     37     struct sockaddr_in client_addr;
     38     socklen_t cli_len = sizeof(client_addr);
     39 
     40     // 创建epoll树根节点
     41     int epfd = epoll_create(2000);
     42     // 初始化epoll树
     43     struct epoll_event ev;
     44 
     45     // 设置边沿触发
     46     ev.events = EPOLLIN | EPOLLET;
     47     ev.data.fd = lfd;
     48     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
     49 
     50     struct epoll_event all[2000];
     51     while(1)
     52     {
     53         // 使用epoll通知内核fd 文件IO检测
     54         int ret = epoll_wait(epfd, all, sizeof(all)/sizeof(all[0]), -1);
     55         printf("================== epoll_wait =============
    ");
     56 
     57         // 遍历all数组中的前ret个元素
     58         for(int i=0; i<ret; ++i)
     59         {
     60             int fd = all[i].data.fd;
     61             // 判断是否有新连接
     62             if(fd == lfd)
     63             {
     64                 // 接受连接请求
     65                 int cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len);
     66                 if(cfd == -1)
     67                 {
     68                     perror("accept error");
     69                     exit(1);
     70                 }
     71                 // 将新得到的cfd挂到树上
     72                 struct epoll_event temp;
     73                 // 设置边沿触发
     74                 temp.events = EPOLLIN | EPOLLET;
     75                 temp.data.fd = cfd;
     76                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
     77                 
     78                 // 打印客户端信息
     79                 char ip[64] = {0};
     80                 printf("New Client IP: %s, Port: %d
    ",
     81                     inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)),
     82                     ntohs(client_addr.sin_port));
     83                 
     84             }
     85             else
     86             {
     87                 // 处理已经连接的客户端发送过来的数据
     88                 if(!all[i].events & EPOLLIN) 
     89                 {
     90                     continue;
     91                 }
     92 
     93                 // 读数据
     94                 char buf[5] = {0};
     95                 int len = recv(fd, buf, sizeof(buf), 0);
     96                 if(len == -1)
     97                 {
     98                     perror("recv error");
     99                     exit(1);
    100                 }
    101                 else if(len == 0)
    102                 {
    103                     printf("client disconnected ....
    ");
    104                     // fd从epoll树上删除
    105                     ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
    106                     if(ret == -1)
    107                     {
    108                         perror("epoll_ctl - del error");
    109                         exit(1);
    110                     }
    111                     close(fd);
    112                     
    113                 }
    114                 else
    115                 {
    116                     // printf(" recv buf: %s
    ", buf);
    117                     write(STDOUT_FILENO, buf, len);
    118                     write(fd, buf, len);
    119                 }
    120             }
    121         }
    122     }
    123 
    124     close(lfd);
    125     return 0;
    126 }

    >gcc lt_epoll.c -o et

    >./et 9876

    (打开另外两个终端,执行./client  9876,然后分别输入数据,看原server终端的接收情况)

    7、测试—边沿非阻塞模式

    >cp et_epoll.c nonblock_et_epoll.c,然后更改:

    >vi nonblock_et_epoll.c

      1 #include <stdio.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <sys/types.h>
      5 #include <string.h>
      6 #include <sys/socket.h>
      7 #include <arpa/inet.h>
      8 #include <ctype.h>
      9 #include <sys/epoll.h>
     10 #include <fcntl.h>
     11 #include <errno.h>
     12 
     13 int main(int argc, const char* argv[])
     14 {
     15     if(argc < 2)
     16     {
     17         printf("eg: ./a.out port
    ");
     18         exit(1);
     19     }
     20     struct sockaddr_in serv_addr;
     21     socklen_t serv_len = sizeof(serv_addr);
     22     int port = atoi(argv[1]);
     23 
     24     // 创建套接字
     25     int lfd = socket(AF_INET, SOCK_STREAM, 0);
     26     // 初始化服务器 sockaddr_in 
     27     memset(&serv_addr, 0, serv_len);
     28     serv_addr.sin_family = AF_INET;                   // 地址族 
     29     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);    // 监听本机所有的IP
     30     serv_addr.sin_port = htons(port);            // 设置端口 
     31     // 绑定IP和端口
     32     bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
     33 
     34     // 设置同时监听的最大个数
     35     listen(lfd, 36);
     36     printf("Start accept ......
    ");
     37 
     38     struct sockaddr_in client_addr;
     39     socklen_t cli_len = sizeof(client_addr);
     40 
     41     // 创建epoll树根节点
     42     int epfd = epoll_create(2000);
     43     // 初始化epoll树
     44     struct epoll_event ev;
     45 
     46     // 设置边沿触发
     47     ev.events = EPOLLIN;
     48     ev.data.fd = lfd;
     49     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
     50 
     51     struct epoll_event all[2000];
     52     while(1)
     53     {
     54         // 使用epoll通知内核fd 文件IO检测
     55         int ret = epoll_wait(epfd, all, sizeof(all)/sizeof(all[0]), -1);
     56         printf("================== epoll_wait =============
    ");
     57 
     58         // 遍历all数组中的前ret个元素
     59         for(int i=0; i<ret; ++i)
     60         {
     61             int fd = all[i].data.fd;
     62             // 判断是否有新连接
     63             if(fd == lfd)
     64             {
     65                 // 接受连接请求
     66                 int cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len);
     67                 if(cfd == -1)
     68                 {
     69                     perror("accept error");
     70                     exit(1);
     71                 }
     72                 // 设置文件cfd为非阻塞模式
     73                 int flag = fcntl(cfd, F_GETFL);
     74                 flag |= O_NONBLOCK;
     75                 fcntl(cfd, F_SETFL, flag);
     76 
     77                 // 将新得到的cfd挂到树上
     78                 struct epoll_event temp;
     79                 // 设置边沿触发
     80                 temp.events = EPOLLIN | EPOLLET;
     81                 temp.data.fd = cfd;
     82                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
     83                 
     84                 // 打印客户端信息
     85                 char ip[64] = {0};
     86                 printf("New Client IP: %s, Port: %d
    ",
     87                     inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)),
     88                     ntohs(client_addr.sin_port));
     89                 
     90             }
     91             else
     92             {
     93                 // 处理已经连接的客户端发送过来的数据
     94                 if(!all[i].events & EPOLLIN) 
     95                 {
     96                     continue;
     97                 }
     98 
     99                 // 读数据
    100                 char buf[5] = {0};
    101                 int len;
    102                 // 循环读数据
    103                 while( (len = recv(fd, buf, sizeof(buf), 0)) > 0 )
    104                 {
    105                     // 数据打印到终端
    106                     write(STDOUT_FILENO, buf, len);
    107                     // 发送给客户端
    108                     send(fd, buf, len, 0);
    109                 }
    110                 if(len == 0)
    111                 {
    112                     printf("客户端断开了连接
    ");
    113                     ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
    114                     if(ret == -1)
    115                     {
    116                         perror("epoll_ctl - del error");
    117                         exit(1);
    118                     }
    119                     close(fd);
    120                 }
    121                 else if(len == -1)
    122                 {
    123                     if(errno == EAGAIN)
    124                     {
    125                         printf("缓冲区数据已经读完
    ");
    126                     }
    127                     else
    128                     {
    129                         printf("recv error----
    ");
    130                         exit(1);
    131                     }
    132                 }
    133 #if 0
    134                 if(len == -1)
    135                 {
    136                     perror("recv error");
    137                     exit(1);
    138                 }
    139                 else if(len == 0)
    140                 {
    141                     printf("client disconnected ....
    ");
    142                     // fd从epoll树上删除
    143                     ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
    144                     if(ret == -1)
    145                     {
    146                         perror("epoll_ctl - del error");
    147                         exit(1);
    148                     }
    149                     close(fd);
    150                     
    151                 }
    152                 else
    153                 {
    154                     // printf(" recv buf: %s
    ", buf);
    155                     write(STDOUT_FILENO, buf, len);
    156                     write(fd, buf, len);
    157                 }
    158 #endif
    159             }
    160         }
    161     }
    162 
    163     close(lfd);
    164     return 0;
    165 }

    >gcc nonblock_et_epoll.c -o non

    >./non 9876

    (打开另外两个终端,执行./client  9876,然后分别输入数据,看原server终端的接收情况)

    》第一次只有129-130行的代码,断开连接会出现问题:recv error?

    》分析:使用原来的读数据判断,有问题,会出现强行读取了没哟数据的缓冲区,所以把129-130行的代码更改为123-131行的代码。

    8、文件描述符突破1024

    >cat /proc/sys/fs/file-max

    >ulimit -a(查看open files默认为1024)

    >sudo vi /etc/security/limits.conf

    (输入密码打开)

    》limits.conf具体修改如下:(中间Tab缩进;最大不能超过上限;soft为软条件,即可通过命令ulimit -n修改

    设置完成,注销或重启虚拟机配置生效。

    >ulimit -a(查看open files变为8000)

    >ulimit -n 3000

    >ulimit -a(查看open files变为3000)

    注意:在一个终端不能频繁修改,如果要修改,再换个终端!

    四、UDP

    1、UDP通信流程

       

    》UDP通信流程:

    2、UDP服务器端代码实现

    >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     // fd绑定本地的IP和端口
    20     struct sockaddr_in serv;
    21     memset(&serv, 0, sizeof(serv));
    22     serv.sin_family = AF_INET;
    23     serv.sin_port = htons(8765);
    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     struct sockaddr_in client;
    33     socklen_t cli_len = sizeof(client);
    34     // 通信
    35     char buf[1024] = {0};
    36     while(1)
    37     {
    38         int recvlen = recvfrom(fd, buf, sizeof(buf), 0, 
    39                                (struct sockaddr*)&client, &cli_len);
    40         if(recvlen == -1)
    41         {
    42             perror("recvfrom error");
    43             exit(1);
    44         }
    45         
    46         printf("recv buf: %s
    ", buf);
    47         char ip[64] = {0};
    48         printf("New Client IP: %s, Port: %d
    ",
    49             inet_ntop(AF_INET, &client.sin_addr.s_addr, ip, sizeof(ip)),
    50             ntohs(client.sin_port));
    51 
    52         // 给客户端发送数据
    53         sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&client, sizeof(client));
    54     }
    55     
    56     close(fd);
    57 
    58     return 0;
    59 }

    >gcc server.c -o server

    3、UDP客户端代码实现

    >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     // create socket
    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 serv;
    21     memset(&serv, 0, sizeof(serv));
    22     serv.sin_family = AF_INET;
    23     serv.sin_port = htons(8765);
    24     inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);//点分十进制转整型,存入第三个参数
    25 
    26     // 通信
    27     while(1)
    28     {
    29         char buf[1024] = {0};
    30         fgets(buf, sizeof(buf), stdin);//从终端输入字符串放入buf中
    31         // buf数据的发送 - server - IP port
    32         sendto(fd, buf, strlen(buf)+1, 0, (struct sockaddr*)&serv, sizeof(serv));
    33 
    34         // 等待服务器发送数据过来
    35         recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL);
    36         printf("recv buf: %s
    ", buf);
    37     }
    38     
    39     close(fd);
    40 
    41     return 0;
    42 }

    >gcc client.c -o client

    >./server

    (打开另外一个终端,执行./client,然后输入数据,看原server终端的接收情况)

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

  • 相关阅读:
    PyMongo系列一:操作MongoDB
    MongoDB副本集配置系列十一:MongoDB 数据同步原理和自动故障转移的原理
    MongoDB副本集配置系列十:MongoDB local库详解和数据同步原理
    MongoDB副本集配置系列九:MongoDB 常见问题
    MongoDB副本集配置系列八:MongoDB监控
    MySQL模拟:线上误update的恢复
    Atlas+Keepalived系列二:管理Atlas
    BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server)
    数据仓库设计小知识之一个属性的维度设计
    Microsoft 家族新成员 Datazen 移动BI 介绍
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_Linux_13.html
Copyright © 2020-2023  润新知