7-阻塞IO下的TTCP实验
本章是基于echo的应用场景来测试,来展示阻塞IO可能会由于协议设计缺陷导致永久阻塞。
echo协议及具体实现
echo协议:客户端往服务端每发送一次数据,服务端就将收到的数据原封不动的发送会客户端。
-
客户端:
通过命令行参数获取参数len,数据的长度len。然后往服务端一次过发送len字节的数据,发送(send)完毕后,就从服务端接收(recv)返回的数据并打印。
这里的send和recv都是阻塞IO。
-
服务端:
每当有一个客户端,就新建一个线程(c++11的线程)来处理。线程函数中,主要逻辑在while循环中,阻塞read 4KB数据,然后将收到数据发回客户端。
测试步骤
在本机测试,在两个终端启动客户端和服务端。当发送数据为1KB,1MB,10MB时,不会阻塞。

发送大约20MB时,会阻塞

用netstat来查看原因,见下图。服务端接收缓冲区有大约6.1MB数据,发送缓冲区有大约2.6MB数据。客户端接收缓冲区有大于0.96MB数据,发送缓冲区有大约4.1数据。

查看内核中TCP接收和发送缓冲最大大小(可设置改变)。接收缓冲区为大约6.29MB,发送缓冲区为大约4.2MB。

客户端与服务端发送数据的流程:
-
客户端调用send,将应用层的数据拷贝到内核的发送缓冲区,待发送;当服务端的内核接收缓冲区未满,客户端将内核发送缓冲区真正发送到服务端
-
服务端的内核接收缓存区接收到数据。当服务端调用recv后,将数据从内核接收缓冲区拷贝到应用层
-
服务端调用send,将应用层的数据拷贝到内核的发送缓冲区,待发送;当客户端的内核接收缓冲区未满,服务端将内核发送缓冲区真正发送到客户端
-
客户端的内核接收缓存区接收到数据。当客户端调用recv后,将数据从内核接收缓冲区拷贝到应用层

协议设计
从程序的设计考虑,客户端发送一个20MB的数据是合法的,而服务端预先不知客户端请求数据的大小,采用了读取4K就响应一次的方式,最后导致的阻塞。
因此我们在设计应用层协议时,在发送数据之前(无论是客户端向服务端发送,还是服务端向客户端发送),先发送一个header,表示接下来要发送多大的数据。这样接收方,接收并解析header,获取到数据的大小为len。接下来,在读到len字节数据之前一直recv,直至接收了len字节数据。然后才把完整的消息解析并发送回接收方。
参考:

浙公网安备 33010602011771号