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,转载请注明出处。

原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_Linux_13.html