· TCP连接管理
TCP连接要解决3个问题:
1. 使对方知道自己的存在,且确认双方能发送能接收;
2. 允许双方协商一些参数(如最大窗口值,是否用窗口扩大选项和时间戳选项等);
3. 分配运输资源(缓存大小,连接表中的项目);
连接建立需要3个报文:
下面是连接的状态变化
在连接之前,A和B会先创建传输控制块TCB,存储了连接相关的重要信息如 TCP连接表,指向发送和接收缓存的指针,指向重传队列的指针,seq和ack等。
SYN不能携带数据,但需要消耗一个序号。ACK报文是可以携带数据的。
为什么建立连接是两次握手而不是三次?
是为了防止已失效的连接请求报文段传到了B产生错误。具体情境如下:
A 发送的SYN报文丢失,A又重发了一个SYN报文,并建立连接成功,后来关闭了连接,通信结束。但丢失的SYN报文此时到达B,B发送第二次握手的报文(ACK报文)后直接进入连接状态,而A没有发起建立连接的请求,不会理睬这个ACK报文,但B会一直等待A发送数据报文段过来,B的资源白白浪费。
下面是四次挥手断开连接
需要注意:
通信双方都可以主动发起关闭连接的请求报文。
FIN可以携带数据,但如果不携带数据也会消耗一个序号。
当被动关闭者B进入CLOSE-WAIT状态时,TCP连接处于半关闭状态,此时B可以发送数据,A无法发送数据,但可以接收数据。因此这个状态下,B可能还会继续发送消息给A。
A进入TIME-WAIT状态后,必须经过时间等待计时器设置的时间2MSL后才能进入CLOSED状态。
一个问题:A 为什么必须等待 2MSL (MSL是最长报文段寿命)的时间后才真正关闭连接?
1、防止第四次挥手的ACK丢失后B无法进入CLOSED状态。
假设A在第三次挥手之后直接进入CLOSED,而且最后一个ACK丢失,B会重发第三次挥手,假设A之前的端口是X,这时有两个情况:一个是A之前的端口又开始建立新的连接,那么A收到该FIN报文之后,会回应一个RST报文给B;一个是A之前的端口没有再开启过了,那么B的FIN报文不会得到ACK回应,B会不停的重传。
2、保证本次连接产生的所有报文(FIN、SYN和数据报文)在这2MSL内从网络中消失,不会和新连接的报文发生混淆(尤其是新连接和旧连接的客户端端口是相同的情况下)。
TCP半关闭
半关闭是指建立连接的两端只有其中一端发送FIN报文,关闭双向连接的某一个方向。主动发送FIN的一端之后就无法向对端发送数据,只能接受对端发送的数据和发送ACK报文段。
一端发送FIN报文之后,另一端发送FIN报文之前的连接状态称为“半关闭状态”。
套接字的close()提供了全关闭操作,而shutdown()则提供了半关闭操作,实际应用中半关闭很少用到。
TCP同时打开与关闭
同时打开是指通信双方A和B,A发送SYN报文给B,并在报文段到达B之前,B也发送SYN报文给A。同时打开只会出现在A和B都是服务器端的情况下。
连接建立超时
如果一个客户端发起连接请求时,服务器是关闭的,那么客户端会在连接等待超时后再重新发送SYN报文,并且每次超时,超时时间都会翻倍。这一行为被称为指数回退。
在Linux中net.ipv4.tcp_syn_retries参数可以配置重发SYN的次数,而net.ipv4.tcp_synack_retries则是第二次握手的SYN报文的重发次数。这两个参数通常选择一个较小值5。
TCP有限状态机
TCP中的相关计时器
超时重传计时器:略;
零窗口持续计时器:零窗口时发送探测报文的计时器;
Time-Wait计时器:time-wait等待2MSL的计时器;
保活计时器:防止TCP连接长时间空闲;
发送报文计时器:防止发送方长时间没有发送报文;
PS: 保活计时器⽤来防⽌在TCP连接出现⻓时期的空闲以及判断对方是否故障下线。
保活计时器 通常设置为2⼩时 。若服务器过了2⼩时还没 有收到客户的信息,它就发送探测报⽂段。若发送了10个 探测报⽂段(每⼀个相隔75秒)还没有响应,就假定客户 出了故障,因⽽就终⽌该连接。