在开始运行代码之前还需要进行一些设置,默认情况下接收端的window size很大,实验中很难耗尽。

所以,为了看到实验效果,需要对系统进行一些设置。打开虚拟机中的注册表设置"regedit",然后找到选项"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters",设置"TcpWindowSize"为4096Bytes。

注意,实验结束后,一定要恢复"TcpWindowSize"的原始设置,不然可能会影响正常的网络访问。

关于更多TCP相关的注册表设置,可以参考这个链接。

 

运行效果

下面运行代码,分别输入两个字符"a"和"b",通过Wireshark可以看到,在进行连接确认的时候,接收端已经给出了我们跟新后的可用窗口4096Bytes。

经过第一轮发送后,接收方的window size减少了1000;当两个数据包都处理完成后,window size又恢复到了4096。

 

第二轮测试中,发送端发送"1234567890"十个字符,从接收端的最后一个[ACK]包可以看到,最后接收端window size为1393,此次传输到此结束。

过了一段时间,当慢接收端处理完数据之后,接收端会发送窗口更新,通知发送端可以窗口为4096Bytes。

 

第三轮测试中,发送端发送更多的字符"1234567890987654321",这次接收端的可用窗口就被耗尽了,然后接收端发送一个[TCP ZeroWindow]的通知;这时,发送端停止发送,然后通过发送窗口探查。

当接收端有可用窗口的时候,接收端会发送窗口更新,数据传输继续。

注意,[TCP ZeroWindowProbe]和[TCP ZeroWindowProbeAck]的Seq和Ack号。

 

糊涂窗口综合症

基于窗口的流量控制方案,会导致一种"糊涂窗口综合症SWS(Silly Window Syndrome)"的状况。

当发送端应用进程产生数据很慢、或接收端应用进程处理接收缓冲区数据很慢,或二者兼而有之;就会使应用进程间传送的报文段很小,特别是有效载荷很小。 极端情况下,有效载荷可能只有1个字节;而传输开销有40字节(20字节的IP头+20字节的TCP头),加上物理帧头后,有效的数据传输比例就更小了,这就浪费了网络带宽,表现为糊涂窗口综合症。

糊涂窗口综合症可能由接收端或者发送端引起,不同的起因需要不同的解决方案,更多内容可以参考此处。

保活定时器

跟据TCP协议,当发送端和接收端都不主动释放一个TCP连接的时候,该连接将一直保持。即使一端出现了故障,由于另一端没有收到任何通知,TCP连接也会一直保持,这样就会造成TCP连接资源的浪费。

TCP keepalive

为了解决这个问题,大多数的实现中都是使服务器设置保活计时器。

保活计时器通常设置为2小时。若服务器过了2小时还没有收到客户的信息,它就发送探测报文段。若发送了10个探测报文段(每一个相隔75秒)还没有响应,就假定客户出了故障,因而就终止该连接。

在Linux系统中,有三个跟TCP keepalive相关的参数:

tcp_keepalive_intvl (integer; default: 75; since Linux 2.4)
The number of seconds between TCP keep-alive probes.
tcp_keepalive_probes (integer; default: 9; since Linux 2.2)
The maximum number of TCP keep-alive probes to send before giving up and killing the connection if no
response is obtained from the other end.
tcp_keepalive_time (integer; default: 7200; since Linux 2.2)
The number of seconds a connection needs to be idle before TCP begins sending out keep-alive probes. Keep-
alives are sent only when the SO_KEEPALIVE socket option is enabled. The default value is 7200 seconds (2
hours). An idle connection is terminated after approximately an additional 11 minutes (9 probes an interval
of 75 seconds apart) when keep-alive is enabled.

在Socket编程中,可以通过设置"TCP_KEEPCNT","TCP_KEEPIDLE"和"TCP_KEEPINTVL"选项来更改上述的三个系统参数:

from socket import *
import time
HOST = "192.168.56.102"
PORT = 8081
ADDR = (HOST, PORT)
client = socket(AF_INET, SOCK_STREAM)
#TCP_KEEPCNT overwrite tcp_keepalive_probes,默认9(次)
#TCP_KEEPIDLE overwrite tcp_keepalive_time,默认7200(秒)
#TCP_KEEPINTVL overwrite tcp_keepalive_intvl,默认75(秒)
client.setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1)
client.setsockopt(SOL_TCP, TCP_KEEPCNT, 5)
client.setsockopt(SOL_TCP, TCP_KEEPINTVL, 5)
client.setsockopt(SOL_TCP, TCP_KEEPIDLE, 10)
client.connect(ADDR)
while True:
input = raw_input()
if input:
client.send(input*1000)
else:
client.close()
break




相关内容