Linux高并发网络编程开发——tcp状态转换-select-poll
在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
10-Linux系统编程-第12天(tcp状态转换-select-poll)
一、学习目标
1、能够描述TCP通信过程中主要状态
2、独立使用select实现IO多路转接
3、理解使用poll实现IO多路转接操作流程
二、复习
(上篇是客户端主动断开连接,此图是服务器端主动断开连接,二者皆可以!)
三、TCP状态转换
1、recv和send函数
相对于read和write,还有recv和send函数,对比如下:
2、TCP状态转换
(注意:需要左右图对比看,其中红线为Client端的状态转换,虚线为Server端的状态转换。)
右图中有些状态可以捕捉到,有些捕捉不到,可通过netstat命令查看,参看——5、nestat命令
3、2MSL等待时长
4、半关闭
一般使用close即可,用不到shutdown半关闭,除非做了文件描述符重定向、文件描述符复制,需要半关闭。
5、netstat命令
(打开多个终端,一个终端开启服务器进程,另几个终端开启客户端进程,然后打开另一个终端,输入:netstat -apn | grep 端口号,查看所有的某个端口号的网络状态信息(如:ESTABLISHED,COLSE_WAIT,FIN_WAIT2)。)
6、端口复用设置
》问题:当服务器端主动断开连接(Ctrl+c),然后再次运行服务器端程序,运行不了,如果检测端口,发现:bind error: Address already in use(端口仍被占用)?
》分析:主动断开的一方有个断开时长MSL,1分钟后再次启动就会正常。
》有没有更好的解决方案呢?端口复用
具体setsockopt 函数及参数参看《Unix网络编程》第7章7.2!
测试(server.c)
1 #include <stdio.h> 2 #include <ctype.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <string.h> 8 #include <arpa/inet.h> 9 #include <sys/socket.h> 10 11 // server 12 int main(int argc, const char* argv[]) 13 { 14 // 创建监听的套接字 15 int lfd = socket(AF_INET, SOCK_STREAM, 0); 16 if(lfd == -1) 17 { 18 perror("socket error"); 19 exit(1); 20 } 21 22 // 绑定 23 struct sockaddr_in serv_addr; 24 memset(&serv_addr, 0, sizeof(serv_addr)); 25 serv_addr.sin_family = AF_INET; 26 serv_addr.sin_port = htons(9999); 27 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 本地多有的IP 28 // 127.0.0.1 29 // inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr); 30 31 // 设置端口复用 32 // 需要在bind函数之前设置 33 int opt = 1; 34 setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt)); 35 36 // 绑定端口 37 int ret = bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 38 if(ret == -1) 39 { 40 perror("bind error"); 41 exit(1); 42 } 43 44 // 监听 45 ret = listen(lfd, 64); 46 if(ret == -1) 47 { 48 perror("listen error"); 49 exit(1); 50 } 51 52 // 阻塞等待连接请求, 并接受连接请求 53 struct sockaddr_in clien_addr; 54 socklen_t clien_len = sizeof(clien_addr); 55 int cfd = accept(lfd, (struct sockaddr*)&clien_addr, &clien_len); 56 if(cfd == -1) 57 { 58 perror("accetp error"); 59 exit(1); 60 } 61 62 char ipbuf[128]; 63 printf("client iP: %s, port: %d\n", inet_ntop(AF_INET, &clien_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)), 64 ntohs(clien_addr.sin_port)); 65 66 char buf[1024] = {0}; 67 while(1) 68 { 69 // read data 70 // int len = read(cfd, buf, sizeof(buf)); 71 int len = recv(cfd, buf, sizeof(buf), 0); 72 if(len == -1) 73 { 74 perror("recv error"); 75 exit(1); 76 } 77 else if(len == 0) 78 { 79 printf("客户端已经断开连接。。。\n"); 80 break; 81 } 82 printf("read buf = %s\n", buf); 83 // 小写转大写 84 for(int i=0; i<len; ++i) 85 { 86 buf[i] = toupper(buf[i]); 87 } 88 printf("after buf = %s\n", buf); 89 90 // 大写串发给客户端 91 // write(cfd, buf, strlen(buf)+1); 92 ret = send(cfd, buf, strlen(buf)+1, 0); 93 if(ret == -1) 94 { 95 perror("send error"); 96 exit(1); 97 } 98 } 99 100 close(cfd); 101 close(lfd); 102 103 return 0; 104 105 }
>gcc server.c -o server
>./server
(这时候在server的终端Ctrl+c结束,立马就可以再次运行)
7、IO多路转接
》IO操作方式
》解决方案?
8、内核大致是如何实现IO转接的
9、select的参数和返回值
》为什么文件描述符最大为1024位?
看内部数组是实现的代码片段:
》文件描述符操作函数:(掌握函数调用)
10、select工作过程
11、select伪代码
》select多路转接伪代码:
1 int main() 2 { 3 int lfd = socket(); 4 bind(); 5 listen(); 6 7 //创建- 文件描述符表 8 fd_set reads, temp; 9 //初始化 10 fd_zero(&reads); 11 //监听的lfd加入到读集合 12 fd_set(lfd, &reads); 13 int maxfd = lfd; 14 15 while(1) 16 { 17 //委托检测 18 temp = reads; 19 int ret = select(maxfd + 1, &temp, NULL, NULL, NULL); 20 21 //是不是监听的 22 if(fd_isset(lfd, &temp)) 23 { 24 //接受新连接 25 int cfd = accept(); 26 //cfd加入读集合 27 fd_set(cfd, &reads); 28 //更新maxfd 29 maxfd = maxfd < cfd ? cfd : maxfd; 30 } 31 //客户端发送数据 32 for(int i = lfd + 1; i <= maxfd; ++i) 33 { 34 if(fd_isset(i, &temp)) 35 { 36 int len = read(); 37 if(len == 0) 38 { 39 //cfd从读集合中del 40 fd_clr(i, &reads); 41 } 42 write(); 43 } 44 } 45 46 } 47 48 }
12、select代码实现
13、poll函数介绍
14、poll实现IO转接代码分析
在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。