首页 > 系统相关 >Linux高并发网络编程开发——epoll-udp

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

时间:2022-11-08 20:07:02浏览次数:43  
标签:serv udp addr epoll int fd Linux include

在学习Linux高并发网络编程开发总结了笔记,并分享出来。

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\n", 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("客户端已经断开了连接\n");
38 close(info->fd);
39 break;
40 }
41 else
42 {
43 printf("recv buf: %s\n", 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\n");
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 ......\n");
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端)

Linux高并发网络编程开发——epoll-udp_#include

>quit

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

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

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

 

2、TCP状态转换复习

Linux高并发网络编程开发——epoll-udp_其他_02

 

三、epoll

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

Linux高并发网络编程开发——epoll-udp_客户端_03

Linux高并发网络编程开发——epoll-udp_#include_04

Linux高并发网络编程开发——epoll-udp_其他_05

 

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\n");
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 ......\n");
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\n",
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 ....\n");
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\n", 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三种工作模式

Linux高并发网络编程开发——epoll-udp_#include_06

Linux高并发网络编程开发——epoll-udp_数据_07

 

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\n");
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 ......\n");
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 =============\n");
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\n",
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 ....\n");
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,只有满了才会输出,除非遇到“\n”
113 // printf(" recv buf: %s\n", 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\n");
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 ......\n");
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 =============\n");
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\n",
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 ....\n");
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\n", 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\n");
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 ......\n");
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 =============\n");
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\n",
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("客户端断开了连接\n");
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("缓冲区数据已经读完\n");
126 }
127 else
128 {
129 printf("recv error----\n");
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 ....\n");
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\n", 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行的代码。

Linux高并发网络编程开发——epoll-udp_#include_08

 

8、文件描述符突破1024

Linux高并发网络编程开发——epoll-udp_数据_09

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

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

>sudo vi /etc/security/limits.conf

(输入密码打开)

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

Linux高并发网络编程开发——epoll-udp_数据_10

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

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

Linux高并发网络编程开发——epoll-udp_客户端_11

>ulimit -n 3000

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

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

 

四、UDP

1、UDP通信流程

Linux高并发网络编程开发——epoll-udp_数据_12

   

Linux高并发网络编程开发——epoll-udp_客户端_13

》UDP通信流程:

Linux高并发网络编程开发——epoll-udp_其他_14

 

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\n", buf);
47 char ip[64] = {0};
48 printf("New Client IP: %s, Port: %d\n",
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\n", buf);
37 }
38
39 close(fd);
40
41 return 0;
42 }

>gcc client.c -o client

>./server

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

 

 

在学习Linux高并发网络编程开发总结了笔记,并分享出来。

标签:serv,udp,addr,epoll,int,fd,Linux,include
From: https://blog.51cto.com/u_15405812/5834765

相关文章

  • Linux高并发web服务器开发——web服务器 - 1
    在学习Linux高并发web服务器开发总结了笔记,并分享出来。11_服务器开发-第01天(web服务器-1)  一、复习       二、html1、html介绍》HTML简介:​​https://www.w3s......
  • Linux高并发网络编程开发——网络编程基础-socket
    在学习Linux高并发网络编程开发总结了笔记,并分享出来。10-Linux系统编程-第10天(网络编程基础-socket)目录:一、Linux网络编程阶段二、网络基础1、网络开发两种设计模式2、协......
  • Linux系统编程——进程控制
    在学习Linux系统编程总结了笔记,并分享出来。09-linux-day05(进程控制)目录:一、学习目标二、进程1、进程和程序2、单道和多道程序设计3、进程的状态转化4、MMU的作用5、PCB......
  • Linux系统编程——信号
    在学习Linux系统编程总结了笔记,并分享出来。09-linux-day07(信号)目录:一、学习目标二、进程通信——信号1、信号的概念回顾2、阻塞信号集、未决信号集、信号产生3、raise和a......
  • Linux系统编程——进程间通信
    在学习Linux系统编程总结了笔记,并分享出来。09-linux-day06(进程间通信)目录:一、学习目标二、进程通信——管道1、管道的概念2、管道通信举例3、父子进程实现ps、grep命令4......
  • Linux命令基础——makefile+gdb+IO
    在学习Linux命令基础总结了笔记,并分享出来。08-linux-day03(makefile-gdb-IO)目录:附:ftp工具介绍——FlashFXP一、学习目标二、makefile1、makefile编写12、makefile编写23、......
  • Linux命令基础——08-linux-day02(vim-gcc-library)
    在学习Linux命令基础总结了笔记,并分享出来。08-linux-day02(vim-gcc-library)目录:一、学习目标二、vim1、vim光标的移动2、vim删除内容3、vim复制粘贴与可视模式4、vim查找......
  • Linux命令基础——stat-readdir-dup2
    在学习Linux命令基础总结了笔记,并分享出来。08-linux-day04(stat-readdir-dup2)目录:一、学习目标二、文件和目录操作1、打开最大文件数量2、stat函数介绍3、stat函数介绍2与......
  • Linux命令基础——vim+gcc+ibrary
    在学习Linux命令基础总结了笔记,并分享出来。08-linux-day02(vim-gcc-library)目录:一、学习目标二、vim1、vim光标的移动2、vim删除内容3、vim复制粘贴与可视模式4、vim查找......
  • Linux高并发网络编程开发——tcp三次握手-并发
    在学习Linux高并发网络编程开发总结了笔记,并分享出来。10-Linux系统编程-第11天(tcp三次握手-并发)  一、学习目标1、熟练掌握三次握手建立连接过程2、熟练掌握四次挥手断开......