socket套接字的超时控制

socket套接字的超时控制

TCP和UDP这两种协议都要求通信双方创建socket套接字,尤其是TCP协议是面向连接的、可靠的、基于字节流的全双工通信,所以要求通信双方要建立连接之后才可以收发数据。流程如下:

首先客户端向服务器发起连接请求,然后服务器接受客户端的连接请求,此时完成TCP三次握手,表示成功建立连接。但是还存在两种情况:

服务器默认设置是阻塞模式,服务器运行之后如果一直没有客户端发起连接请求,此时服务器会阻塞在accept()函数的位置,这样会导致服务器程序无法继续向下执行。

把服务器的套接字设置为非阻塞模式,如果服务器没有监听到有客户端发起连接请求,则服务器的accept()函数会立刻返回。

这两种情况都过于绝对,所以Linux系统提供了超时机制避免这两种情况导致程序逻辑异常。

超时控制属于套接字文件的属性选项之一,需要调用setsockopt()函数进行设置,规则如下:

可以看到,当套接字设置为非阻塞模式并且设置了超时时间,超时时间内没有收发任何数据并且超时时间到达,则会立刻返回-1。如果超时时间设置为0,则操作永远不会超时。

另外,如果要设置超时时间,需要利用名称叫做struct timeval的结构体,该结构体定义在time.h头文件中。

可以看到结构体有2个成员,成员tv_sec指的是秒数,成员tv_usec指的是微秒数,如果服务器端设置了接收超时,并且假设超时时间为n秒,则有两种情况:

注意:如果服务器还未和客户端建立连接,并且服务器处于监听状态,则最多等待对端n秒。如果服务器已经和客户端建立连接,则服务器等待客户端发送数据的时间最多是n秒。

练习:设计2个程序,分为UDP客户端和UDP服务器,对于UDP客户端作为发送数据的一端,UDP服务器作为接收数据的一端,要求把UDP服务器设置为阻塞模式,并设置超时时间为5s,运行程序观察效果。

二、服务器的IO调度策略

不管是基于UDP实现的服务器或者是基于TCP实现的服务器,都需要处理多个客户端请求。

  • 基于UDP实现

由于UDP协议不需要连接,因此服务端只需要一个套接字便可与任意个客户端通信,但默认的套接字是阻塞型的,这意味着当客户端没有发来消息的时候,服务端如果调用 recvfrom() 将会进入不限期阻塞而无法做别的事情。

  • 基于TCP实现

对于TCP服务器而言,每当有一个远端的客户端发起连接成功后,服务端这边都会新增一个已连接套接字与之匹对,那么随着客户端连接的越来越多,服务端要处理的套接字也随之增多,如何同时妥善处理这些套接字成为必须解决的问题。

这就涉及到服务器要采用何种策略对客户端进行调度,服务器的调度策略也被称为服务器IO模型,指的是当一个服务端网络程序需要同时处理多个套接字时,采取什么方案去妥善处理它们。

  1. 非阻塞轮询(不合理)

一种方案就是将所有的套接字都设置为非阻塞模式,这样就不用担心客户端不发出数据导致服务器端卡死的问题,但是非阻塞套接字也无法妥善地告知服务器数据何时到达,所以服务器需要不断地尝试读取客户端数据,这就是采用轮询方式实现。

由于非阻塞模式属于套接字的属性,而套接字在Linux系统属于文件,所以通过Linux提供了fcntl()函数可以设置或者获取套接字文件的属性。

注意:为了防止套接字文件的原有属性被破坏,所以一定要先用 F_GETFL 获取套接字已有属性,然后通过位或运算加上非阻塞属性 O_NONBLOCK,然后再用 F_SETFL 进行设定。

练习:设计一个程序,要求创建UDP套接字,并设置UDP套接字为非阻塞模式,然后采用轮询方式来接收客户端信息。

  1. 多任务并发(很合理)

多任务并发模型就是利用多进程或者多线程来达到同时处理多个套接字的目的。一般而言,进程用于具有较完整逻辑块的整合。如果只是处理网络套接字的数据,那么一般使用多线程。

对于UDP而言,由于不存在连接的问题,因此服务端一个UDP套接字可以接收任意的客户端发来的数据,可直接将该套接字交由一条专用于收发数据的线程管理即可。

对于TCP而言,首先创建一条专门的线程处理监听套接字,用于随时监听和接受客户端的连接请求。另外由于每当有一个客户端连接成功,服务端都会产生一个新的连接套接字来与之通信,那么就应该每产生一个套接字就分配一条线程与之对应,便可形成多任务并发的服务器IO模型。

提示:可以使用链表来记录客户端的信息,当客户端断开连接,则从链表中删除客户端信息。

void *client_handler(void *arg) { //用于记录和客户端通信的套接字

int client_fd = *(int *)arg;

for(;;)

{

recv(); //等待接收客户端的消息,没有则阻塞

}

}

int main()

{

//1.创建TCP套接字

int tcp_fd = socket();

//2.调用bind函数绑定主机信息到套接字

bind();

//3.设置套接字为监听状态

listen();

//4.循环接收客户端的连接请求,没有收到连接请求,就阻塞

for(;;)

{

int new_fd = accept();

pthread_create(,,,(void *)&new_fd);

}

}

  1. 异步信号(适合UDP)

指的是用信号来驱使服务器妥善处理多个远端套接字,每当远端有数据到达,那么就在本端触发信号,然后利用信号的异步特性来处理这些远端信息。

注意: SIGIO信号 默认会杀死目标进程,因此必须要设定其响应函数。另外,SIGIO信号由内核针对套接字产生,而内核套接字可以在多个应用层程序中有效(例如父进程将套接字遗传给各个子进程),因此必须指定该信号的宿主。

默认情况下,套接字收到数据时不会触发 SIGIO信号,所以必须将套接字设定为异步工作模式,它才会触发该信号。

由于不管套接字收到何种数据,内核都会触发 SIGIO信号,但是这种方式却不适用于 TCP 协议。

因此在 TCP 中,当客户端发来连接请求、普通数据、数据回执,甚至是断开请求、断开请求的回执等等情况,都触发一样的信号,这就使得服务端光凭这一个信号无法知道下一步要做什么,因此信号驱动的服务器模型,一般只适用于UDP协议。

  1. 多路复用(推荐使用)

多路复用指的是通过某个特定的接口来同时监听多路阻塞IO,这就达到既无需多进程多线程,又可以同时处理多个阻塞套接字的目的。

Linux系统中提供了两个函数select()或poll()实现同时监控多个套接字,当发现一个或多个套接字的某种状态就绪(读状态、写状态)时,再调用相应的函数去处理的过程。

三个套接字集合分别关注三个不同的就绪状态,如果需要同时监控某个套接字sockfd的不同就绪状态,则需要将此套接字放入相应的套接字集合中。

注意:当 select() 返回时,三个集合中未处于就绪状态的套接字将被自动清零,因此如果要重复监控它们就需要重新设置这些套接字集合。

int main()

{

//1.创建UDP套接字

int udp_fd = socket();

//2.调用bind绑定主机信息

//3.进入循环,利用select进行套接字监听

for(;;)

{

struct timeval timeout;

timeout.tv_sec = 5; //设置超时时间

timeout.tv_usec = 0;

fd_set set;

FD_ZERO(&set); //清空集合

FD_SET(udp_fd,&set); //把套接字的文件描述符放入集合中

//对集合中的文件描述符进行监测 读就绪状态

int ret = select(udp_fd+1,&set,NULL,NULL,&timeout);

//错误处理

if(-1 == ret)

{

}

//是否超时

if(0 == ret)

{

}

//读取客户端发送的消息,并且该函数是不会阻塞的

recvfrom();

}

}

int main()

{

//1.创建TCP套接字

int tcp_fd = socket();

//2.调用bind绑定主机信息

//3.设置套接字为监听状态

listen();

//4.死循环

for(;;)

{

//5.进行套接字的select多路复用

struct timeval timeout;

timeout.tv_sec = 5; //设置超时时间

timeout.tv_usec = 0;

fd_set set;

FD_ZERO(&set); //清空集合

FD_SET(udp_fd,&set); //把套接字的文件描述符放入集合中

//对集合中的文件描述符进行监测 读就绪状态

int ret = select(tcp_fd+1,&set,NULL,NULL,&timeout);

//错误处理

if(-1 == ret)

{

}

//是否超时

if(0 == ret)

{

}

//调用accept()接受客户端连接请求

int clientfd = accept();

......

}

}

三、wireshark网络抓包工具

大家已经知道网络中的数据包在输出时会经过各个协议层,发送端应用层的数据在经过下面协议层时会进行封包操作,比如采用TCP协议发送数据时应用层的的数据经过传输层时会添加TCP头,经过网络层时会添加IP头.....,而接收端会把经过封包的数据包进行拆包从而得到有效数据。

如果用户想要对数据包进行分析,则可以使用网络抓包工具实现,比较主流的一款软件就叫做wireshark。

wireshark软件是非常流行的网络封包分析软件,简称小鲨鱼,功能十分强大。可以截取各种网络封包,显示网络封包的详细信息。并且wireshark是开源软件,可以运行在Windows平台和Linux平台。

  • 网卡选择

软件安装完成后需要选择要监控的网络接口,比如物理网卡、虚拟网卡等,操作界面如下图:

一般笔记本电脑都会存在两个物理网卡:有线网卡(网线)和无线网卡(WIFI),如果电脑安装过虚拟机,则虚拟机会提供虚拟网卡(VMnet1和VMnet8),用户可以选择要监控的网卡。

  • 网络通信

网络通信一般要经过物理网卡,比如两台主机(物理机和物理机、虚拟机和虚拟机)之间进行通信,如果双方主机都采用有线通信的话,则可以监控对应的有线网卡。

如果是虚拟机和虚拟机之间通信,则需要监控虚拟机的虚拟网卡所桥接的有线网卡,如下:

可以通过VMware软件的菜单栏"编辑"选项,然后点击虚拟网络编辑器,查看虚拟网卡桥接的物理网卡。另外,如果虚拟机采用NAT模式,则监控虚拟网卡VMnet8即可。

  • 本地通信

本地通信指的是在同一台主机上的两个网络进程之间的通信,如果是虚拟机上的两个程序采用网络通信,则数据包是不会经过物理网卡转发的,而是使用本地环回地址转发,所以需要在虚拟机中安装wireshark软件来监控lo网卡。

Ubuntu系统安装wireshark软件流程:开启终端之后输入sudo apt-get install wireshark即可:

如果安装过程中提示是否配置wireshark命令行,则点击yes,安装完成后可以启动该软件。

练习:完成在linux系统和Windows系统中安装Wireshark网络抓包工具,安装后启动测试。

  • 筛选数据

当使用wireshark监控某个网卡之后,如果有很多数据包都经过该网卡,则想要抓取的数据包可能很难被发现,所以可以设置某些筛选条件来捕获目标数据包。

在wireshark软件中这些筛选条件被称为过滤表达式,过滤表达式一般在wireshark的过滤器中设置,wireshark支持两种过滤器,分别是捕获过滤器和显示过滤器,区别如下:

  1. 捕获过滤器

捕获过滤器是在抓取数据包之前就做出筛选,只抓取符合要求的数据包,捕获过滤器如下图:

比如要监控无线网卡WLAN,并且想要抓取经过该接口的TCP协议的数据包,操作如下所示:

  1. 显示过滤器

显示过滤器指的是从抓取到的大量数据包找到符合条件的数据包并显示出来,可以节省用户的查找时间,比如抓取到很多TCP协议的数据包,但是想从这些数据包中找到指定端口号的数据包,就可以通过显示过滤器实现,操作如下所示:

注意:筛选条件是通过过滤表达式实现的,过滤表达式由过滤项 + 过滤关系 + 过滤值组成。

过滤项一般是指相关协议和协议的相关字段,显示过滤器一般都会有协议的字段提示,如下:

过滤关系是过滤项和过滤值的关系,wireshark的帮助文档中有过滤关系的相关符号,如下:

过滤值指的是过滤项的值,比如IP地址、端口号、帧长度等符合相关协议字段的具体数值!

练习:要求在ubuntu系统中安装抓包工具,然后监控ubuntu的虚拟网卡,然后要求运行接入AI大模型的代码,要求利用抓包工具截取和大模型通信的数据包(提示:HTTP 80)

四、Linux网络相关指令

  1. netstat命令

用户进行网络编程的时候,经常需要借助于一些shell命令来查看某个网卡或某个连接的信息,这样才可以了解网络数据的收发细节。Linux系统中提供了一个netstat命令获取网络相关信息。

-t : 查看TCP协议相关信息(默认只看到处于ESTABLISHED状态的信息)

-u : 查看UDP协议相关信息(默认只看到处于ESTABLISHED状态的信息)

-a : 显示所有信息,包括不处于ESTABLISHED状态的信息

-n : 使用数字显示,而不是别名

-l : 只看处于LISTEN状态的信息

-s : 按协议输出统计信息

-i : 显示活跃网口的信息

-r : 显示路由信息

-c : 持续跟踪输出

-p : 显示与连接相关的进程信息

  1. netcat命令

另外,Linux系统还提供一款名称叫做netcat的网络工具,利用该工具可以用于和TCP或者UDP相关的任何事务,比如打开TCP连接、发送UDP数据包、监听TCP或者UDP的端口等。

  1. ifconfig命令

有些情况需要配置虚拟网卡的参数或者查看当前系统的虚拟网卡相关信息,则可以通过ifconfig命令实现,比如修改虚拟网卡的IP地址等。

相关推荐
{{uname}}26 分钟前
利用WebSocket实现实时通知
网络·spring boot·websocket·网络协议
懒惰的bit26 分钟前
STM32F103C8T6板子使用说明
stm32·单片机·嵌入式硬件
芯岭技术1 小时前
PY32系列单片机离线烧录器,可配置选项字节和上机台批量烧录
单片机·嵌入式硬件
2501_915909063 小时前
iOS App 安全性探索:源码保护、混淆方案与逆向防护日常
websocket·网络协议·tcp/ip·http·网络安全·https·udp
阿川!4 小时前
嵌入式软件--stm32 DAY7 I2C通讯上
stm32·单片机·嵌入式硬件·mcu
欢乐熊嵌入式编程5 小时前
智能手表项目的《项目背景与目标说明书》样本文档
嵌入式硬件·目标跟踪·规格说明书·智能手表
海尔辛6 小时前
学习黑客BitLocker与TPM详解
stm32·单片机·学习
christine-rr6 小时前
【25软考网工】第六章(4)VPN虚拟专用网 L2TP、PPTP、PPP认证方式;IPSec、GRE
运维·网络·网络协议·网络工程师·ip·软考·考试
小白自救计划7 小时前
网络协议分析 实验四 ICMPv4与ICMPv6
网络·网络协议