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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值