在学习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 #include2 #include 3 #include 4 #include 5 #include <string.h> 6 #include 7 #include 8 #include 9 #include 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端)
>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 #include2 #include 3 #include 4 #include 5 #include <string.h> 6 #include 7 #include 8 #include 9 #include 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 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三种工作模式
5、测试—epoll水平触发模式
>cp epoll.c lt_epoll.c,然后更改:
>vi lt_epoll.c
1 #include2 #include 3 #include 4 #include 5 #include <string.h> 6 #include 7 #include 8 #include 9 #include 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 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 #include2 #include 3 #include 4 #include 5 #include <string.h> 6 #include 7 #include 8 #include 9 #include 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 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 #include2 #include 3 #include 4 #include 5 #include <string.h> 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 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 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行的代码。
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 #include2 #include 3 #include 4 #include 5 #include 6 #include <string.h> 7 #include 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 #include2 #include 3 #include 4 #include 5 #include 6 #include <string.h> 7 #include 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高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。