TCP发送优化(ZeroWindow分析)

通信模型一:

Client 创建一个 TCP 的 socket,并通过 SO_SNDBUF 选项设置它的发送缓冲区大小为 2048Byte,连接到 Server 后,每 1 秒发送一个 TCP报文(1024Byte)。Server 端不调用 recv()。预期的结果则分为以下几个阶段:

阶段1:Server 端的 socket 接收缓冲区未满,所以尽管 Server 不会 recv(),但依然能对 Client 发出的报文回复 ACK;

阶段2: Server 端的 socket 接收缓冲区被填满了,向 Client 端通告零窗口(Zero Window)。Client 端待发送的数据开始累积在 socket 的发送缓冲区;

阶段3: Client 端的 socket 的发送缓冲区满了,用户进程阻塞在 send函数里。

实际执行时,表现出来的现象也"基本"符合预期。不过当我们在 Client 端通过 netstat -an 监控 TCP 连接的发送队列长度时,发现这个值竟然从 0 最终增长到 14480,它轻松地超了之前设置的 SO_SNDBUF 值(2048)

分析

以上就是一个简单的Zero Window产生的模型,简单来说,Zero Window问题就是指在TCP连接中,发送方为了保障可靠传输,会根据接收方反馈的窗口大小来控制发送窗口的大小,但当接收方窗口大小为0时,发送方就会停止发送,从而导致通讯阻断的问题。所以起解决办法有以下几种。

一、增加接收方窗口大小

一种常用的方法是增加接收方的窗口大小。这样可以使得接收方有更多的缓存空间,从而避免出现Zero Window的情况。可以通过修改TCP接收缓冲区大小的方法来解决

bash 复制代码
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int buf_size = 1024 * 1024 * 5; //设置缓存区大小为5MB
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &buf_size, sizeof(buf_size));

二、禁用Nagle算法(默认是打开的)

Nagle算法是一种优化TCP传输的算法,其原理是将小的数据包合并成一个大的数据包再发送,从而减少网络传输量。但是当数据量很小的时候,会导致数据包积压,造成Zero Window问题。可以通过禁用Nagle算法来解决,示例代码如下:

bash 复制代码
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int enable = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&enable, sizeof(enable));

三、增加发送方的等待时间

在一些情况下,由于网络状况不良,导致接收方没有足够的时间处理数据,造成Zero Window的情况。可以通过增加发送方的等待时间,等待接收方处理完成后再发送数据,从而避免出现Zero Window的情况,示例代码如下:

c 复制代码
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct timeval timeout;
timeout.tv_sec = 0; 
timeout.tv_usec = 500*1000; //等500毫秒
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));

四、增加发送方的重传次数

在一些情况下,由于网络状况不良或者其他原因,导致数据丢失或者接收方无法正确接收数据,造成Zero Window的情况。可以通过增加发送方的重传次数,保证数据能够正确传输,从而避免出现Zero Window的情况,示例代码如下:

c 复制代码
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int retry_count = 6; //最多重试6次
setsockopt(sock, SOL_TCP, TCP_SYNCNT, (void*)&retry_count, sizeof(retry_count));

五、使用TCP Keepalive机制

TCP Keepalive机制是一种保持TCP连接的机制,其原理是在TCP连接空闲一段时间后,会向对端发送心跳包,以检测连接是否还存活。可以通过使用TCP Keepalive机制来避免出现Zero Window的情况,示例代码如下:

c 复制代码
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int enable = 1; //开启TCP Keepalive机制
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void*)&enable, sizeof(enable));
相关推荐
Nuyoah11klay2 小时前
华清远见25072班网络编程学习day5
网络
weixin_456904272 小时前
使用HTTPS 服务在浏览器端使用摄像头的方式解析
网络协议·http·https
CyHacker_10102 小时前
网络编程-day4
网络
老六ip加速器3 小时前
手机ip隔离方法
tcp/ip·智能手机·php
疯狂的维修5 小时前
关于Gateway configration studio软件配置网关
网络协议·c#·自动化·gateway
wow_DG5 小时前
【WebSocket✨】入门之旅(五):WebSocket 的安全性
网络·websocket·网络协议
会开花的二叉树6 小时前
UDP Socket 进阶:从 Echo 到字典服务器,学会 “解耦” 网络与业务
服务器·网络·udp
-SGlow-6 小时前
Linux相关概念和易错知识点(45)(网络层、网段划分)
linux·运维·服务器·网络
阿部多瑞 ABU7 小时前
《学校机房终端安全全链条攻防分析与防御体系建设报告》
网络·安全
czhc11400756637 小时前
LINUX913 shell:set ip [lindex $argv 0],\r,send_user,spawn ssh root@ip “cat “
tcp/ip·r语言·ssh