动手学习TCP系列之4种定时器(1)


上一篇中介绍了TCP数据传输中涉及的一些基本知识点。本文让我们看看TCP中的4种定时器。

TCP定时器

对于每个TCP连接,TCP管理4个不同的定时器,下面看看对4种定时器的简单介绍。

重传定时器使用于当希望收到另一端的确认。

该定时器是用来决定超时和重传的。

由于网络环境的易变性,该定时器时间长度肯定不是固定值;该定时器时间长度的设置依据是RTT(Round Trip Time),根据网络环境的变化,TCP会根据这些变化并相应地改变超时时间。

坚持定时器(persist)使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口。

保活定时器(keepalive)可检测到一个空闲连接的另一端何时崩溃或重启。

2MSL定时器测量一个连接处于TIME_WAIT状态的时间。

参见"动手学习TCP:TCP特殊状态"中对TIME_WAIT的介绍

下面就介绍一下坚持定时器和保活定时器。

坚持定时器

TCP通过让接收方指明希望从发送方接收的数据字节数(即窗口大小)来进行流量控制。

如果窗口大小为 0会发生什么情况呢?这将有效地阻止发送方传送数据,直到窗口变为非0为止。

 

但是,由于TCP不对ACK报文段进行确认(TCP只确认那些包含有数据的ACK报文段),如果上图中通知发送方窗口大于0的[ACK]丢失了,则双方就有可能因为等待对方而使连接死锁。接收方等待接收数据(因为它已经向发送方通告了一个非0的窗口),而发送方在等待允许它继续发送数据的窗口更新。

为防止这种死锁情况的发生,发送方使用一个坚持定时器 (persist timer)来周期性地向接收方查询,以便发现窗口是否已增大。这些从发送方发出的报文段称为窗口探查(window probe)。

实验代码

下面通过Python socket实现一个快的发送端和慢的接收端,然后通过Wireshark抓包来看看窗口更新通知和窗口探查。

客户端代码如下,用户输入字符,客户端将用户输入重复1000次然后发送给服务端,通过这种简单的重复来模拟一个快的发送端: 

from socket import *
import time
HOST = "192.168.56.102"
PORT = 8081
ADDR = (HOST, PORT)
client = socket(AF_INET, SOCK_STREAM)
client.connect(ADDR)
while True:
input = raw_input()
if input:
client.send(input*1000)
else:
client.close()
break

对于服务端,通过制定一个小的接收BUFFER,以及一个延时来模拟一个慢的接收端:

import sys
from socket import *
import time
HOST = "192.168.56.102"
PORT = 8081
BUFSIZ = 100
ADDR = (HOST, PORT)
server = socket(AF_INET, SOCK_STREAM)
print "Socket created"
try:
server.bind(ADDR)
except error, msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
server.listen(1)
print 'Socket now listening'
conn, addr = server.accept()
while True:
time.sleep(3)
try:
data = conn.recv(BUFSIZ)
if data:
print data
else:
conn.close()
break
except Exception, e:
print e
break




相关内容