发件人套接字配置了TCP_NODELAY选项.发送器缓冲区(SO_SNDBUF)配置为8MB.接收缓冲区(SO_RCVBUF)也配置为8MB. Tcp窗口缩放已激活.
update-1:我使用zeromq 3.1.1中间件来传输数据.套接字配置,包括TCP_NODELAY标志由中间件执行.有些选项可以访问,例如rx和tx发出缓冲区大小但不是TCP_NODELAY.据我所知,TCP_NODELAY被激活以确保尽可能地发送数据.同时,实际的套接字发送和决定发送消息是在两个单独的线程中执行的.如果在发送批次中的第一条消息时有几条消息可用,则进行适当的批处理.
我使用tcpdump运行捕获,从中提取下面的帧.初始TCP握手后,发送方(172.17.152.124)开始发送数据.接收器和接收器的初始窗口大小是5840字节.发件人为5792字节.
我的问题是发送者发送两帧(#6和#7)然后停止,等待ack从接收器返回.据我所知,接收器的窗口大小没有达到,传输不应该停止(384字节未完成,初始接收窗口大小为5840字节).我开始认为我没有正确理解TCP是什么.有人可以帮忙澄清吗?
update-2:我的数据有效负载由一个幻数后跟一个时间戳组成.我通过比较有效载荷的时间戳和tcpdump放置的时间戳来隔离延迟的数据包.帧#9的有效载荷ts非常接近帧#6和#7的有效载荷,并且明显小于帧#8中接收到的确认的时间戳.
update-1:没有立即发送帧#9的事实可以通过TCP信道的慢启动来解释.实际上,一旦连接运行几分钟,问题也会出现,所以慢启动似乎不是一般的解释.
20:53:26.017415 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [S],seq 2473022771,win 5840,options [mss 1460,sackOK,TS val 4219180820 ecr 0,nop,wscale 8],length 0
20:53:26.017423 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [S.],seq 2948065596,ack 2473022772,win 5792,TS val 186598852 ecr 219180820,wscale 9],length 0
20:53:26.091940 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [.],ack 1,win 23,options [nop,TS val 4219180894 ecr 186598852],length 0
20:53:26.091958 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [P.],seq 1:15,w in 23,TS val 4219180895 ecr 186598852],length 14
20:53:26.091964 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [.],ack 15,win 12,TS val 186598927 ecr 4219180895],length 0
20:53:26.128298 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [P.],seq 1:257,TS val 186598963 ecr 4219180895],length 256
20:53:26.128519 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [P.],seq 257:385,length 128
20:53:26.202465 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [.],ack 257,win 27,TS val 4219181005 ecr 186598963],length 0
20:53:26.202475 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [.],seq 385:1833,TS val 186599037 ecr 4219181005],length 1448
20:53:26.202480 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [P.],seq 1833:2305,length 472
如果这很重要,两端都是Linux RHEL5盒子,2.6.18内核和网卡都使用e1000e驱动程序.
更新3
/etc/sysctl.conf的内容
[jlafaye@localhost ~]$cat /etc/sysctl.conf | grep -v "^#" | grep -v "^$" net.ipv4.ip_forward = 0 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.default.accept_source_route = 0 kernel.sysrq = 0 kernel.core_uses_pid = 1 net.ipv4.tcp_syncookies = 1 kernel.msgmnb = 65536 kernel.msgmax = 65536 kernel.shmmax = 68719476736 kernel.shmall = 4294967296 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.core.rmem_default = 1048576 net.core.wmem_default = 1048576 net.ipv4.tcp_rmem = 65536 4194304 16777216 net.ipv4.tcp_wmem = 65536 4194304 16777216 net.core.netdev_max_backlog = 10000 net.ipv4.tcp_window_scaling = 1 net.ipv4.tcp_mem = 262144 4194304 16777216 kernel.shmmax = 68719476736
解决方法
使用有用的工具ss,我能够检索连接的当前拥塞窗口大小(请参阅输出中的cwnd值):
[user@localhost ~]$/usr/sbin/ss -i -t -e | grep -A 1 56001
ESTAB 0 0 192.168.1.1:56001
192.168.2.1:45614 uid:1001 ino:6873875 sk:17cd4200ffff8804
ts sackscalable wscale:8,9 rto:277 rtt:74/1 ato:40 cwnd:36 send 5.6Mbps rcv_space:5792
我多次运行该工具,发现拥塞窗口大小定期重置为初始值(10毫秒,在我的Linux机器上).连接不断循环回到慢启动阶段.在慢启动期间,具有超过窗口大小的多个消息的突发被延迟,等待与突发的第一个分组相关的ack.
流量由一系列突发组成的事实可能解释了拥塞窗口大小的重置.
通过在空闲期后停用慢启动模式,我能够摆脱延迟.
[user@host ~]$cat /proc/sys/net/ipv4/tcp_slow_start_after_idle 0