【网络协议系列】- TCP连接建立与释放
【网络协议系列】- TCP连接建立与释放
一、TCP报文格式
TCP数据在IP报文中的位置:

TCP报文的数据格式:

-
TCP报文段首部的前20个字节是固定的,后面有4n字节的选项数据,因此TCP报文段的最小长度为20个字节
-
源端口和目的端口: 加上IP首部的源IP地址和目的IP地址,确定唯一的一个TCP连接,通过目的端口来决定TCP将数据报交付哪个应用程序,从而实现TCP的分用功能
-
序号: 占4字节,序号的范围为[0,4284967296]
-
由于TCP是面向字节流的,传送的字节流中的每一个字节都按序编号,序号字段指本报文段所发送的数据的第一个字节的序号
-
序号是循环使用的,当序号增加到最大值时,下一个序号就又回到了0
-
-
确认号: 当ACK标志位为1时有效,表示期望收到的下一个报文段的第一个数据字节的序号
- 确认号为N,则表明到序号N-1为止的所有数据字节都已经被正确地接收到了
-
头部长度: 指出TCP报文首部的长度
- 头部长度占4位,可表示0~15,但它是以4字节为计算单位,因此头部长度的最大值为60字节,意味着选项的长度不大于40字节
-
保留位: 保留着没用,全设为0
-
控制位:
- URG: 与首部中的紧急指针字段配合使用;URG为1,表明紧急指针字段有效,有紧急数据要传送,于是发送方TCP就把紧急数据插入到本报文段数据的最前面,其后才是普通数据
- ACK: 仅当ACK=1时确认号字段才有效,当ACK=0时,确认号无效,TCP规定连接建立后所有的传送报文段都必须把ACK置1
- PSH: PSH为1,则接收方接受到该报文段后,直接将其交付应用进程,而不再等待整个缓存都填满后再向上交付
- RST: 复位标志,RST=1时,表明TCP连接出现严重差错,必须释放连接,重新建立连接
- SYN: 同步序号,用来发起一个连接;当SYN=1且ACK=0时,表明这是一个连接请求报文段,若对方同意建立连接,则在响应的报文段中使SYN=1和ACK=1
- FIN: 用来释放连接,FIN=1表明此报文段的发送方的数据已发送完毕,要求释放连接
-
窗口尺寸: 指接收方让发送方下次发送报文段时设置的发送窗口的大小
-
校验和: 校验范围包括首部和数据这两部分
-
紧急指针: 当URG=1时才有效,指出本报文段中紧急数据的字节数,即使窗口为0也可发紧急数据
-
选项与填充: 选项应为4字节的整数倍,否则用0填充
- 最常见的可选字段是最长报文大小MSS,每个连接方通常都在通信的第一个报文段中指明这个选项,即本端所能接收的TCP报文数据部分的最大长度,MSS不设置则默认536
二、TCP连接建立
TCP三次握手:

- 服务端的TCP进程先创建传输控制块TCB,准备接受客户端进程的连接请求,然后服务端进程处于
LISTEN状态,等待客户端的连接请求 - 客户端的TCP进程也首先创建传输控制模块TCB,然后向服务端发出连接请求报文段
- 该报文段首部中的SWN=1,ACK=0,同时选择一个初始序号seq=i,TCP规定,SYN=1的报文段不能携带数据,但要消耗掉一个序号
- 发送后,TCP客户进程进入
SYN-SENT状态(同步已发送) - 这是TCP连接的第一次握手
- 服务端收到客户端发来的请求报文后,如果同意建立连接,则向客户端发送确认
- 确认报文中的STN=1,ACK=1,确认号ack=i+1,同时为自己选择一个初始序号seq=j
- 同样该报文段也是SYN=1的报文段,不能携带数据,但同样要消耗掉一个序号
- 发送后,TCP服务端进入
SYN-RCVD状态(同步收到) - 这是TCP连接的第二次握手
- TCP客户端进程收到服务端进程的确认后,还要向服务端给出确认
- 确认报文段的ACK=1,确认号ack=j+1,而自己的序号为seg=i+1
- TCP规定,ACK报文段可以携带数据,但如果不携带数据则不消耗序号,即不携带数据,则下一个报文段的序号仍为seq=i+1
- 发送后,则TCP连接已经建立,客户端进入
ESTABLISHED状态(已建立连接) - 这是TCP连接的第三次握手,第三次握手客户端已经可以发送携带数据的报文段了
- 当服务端收到确认后,也进入
ESTABLISHED状态(已建立连接)
双方同时主动连接的TCP连接建立过程:

-
正常情况下,传输连接都是由一方主动发起的,但也有可能双方同时主动发起连接
-
当同时发出连接请求时,两端同时发送一个SYN=1的数据段,并进入
SYN_SENT状态 -
当每一端收到SYN数据段时,状态变为
SYN_RCVD,同时都再发送SYN=1且ACK=1的确认数据段 -
当双方都收到对方的SYN+ACK数据段后,便都进入
ESTABLISHED状态
-
-
此时就会发生连接碰撞,最终成功建立一个TCP连接,而不是两个
- 如果第一个连接请求建立起一个由套接字(IP,port)标识的连接,而第二个连接也建立了这样一个连接,那么在TCP实体内部只有一个套接字表项
为什么要三次握手:
- 前两次的握手显然是必须的,但最后一次,客户端收到服务端发来的确认后为什么还要向服务端再发送一次确认呢?
- 这主要是为了防止已失效的请求报文段突然又传送到了服务端而产生连接的误判
- 如果客户端发送了一个连接请求报文段到服务端,但是在某些网络节点上长时间滞留了,而后客户端又超时重发了一个连接请求报文段该服务端,而后正常建立连接,数据传输完毕,并释放了连接。如果这时候第一次发送的请求报文段延迟了一段时间后,又到了服务端
- 很显然,这本是一个早己失效的报文段,但是服务端收到后会误以为客户端又发出了一次连接请求,于是向客户端发出确认报文段,并同意建立连接
- 假设不采用三次握手,这时服务端只要发送了确认,新的连接就建立了,但由于客户端不会理会服务端的确认,也不会向服务端发送数据,而服务端却认为新的连接已经建立了,这样服务端就会一直等待下去,真到超出保活计数器的设定值,将客户端判定为出问题,才会关闭连接
- 这样浪费了很多服务器资源,而如果采用三次握手,客户端就不会向服务端发出确认,服务端由与收不到确认,就知道客户端没有要求建立连接,从而不建立该连接
三、TCP连接释放
TCP四次挥手:

数据传输结束后,通信的双方都可以释放连接,并停止发送数据,假设现在客户端和服务端都处于ESTABLISHED状态
- 客户端A向服务端发出连接释放报文段,主动关闭TCP连接
- 释放连接报文段中FIN=1,序号为seq=u,该序号等于前面已经传送过去的数据的最后一个字节的序号+1,TCP规定,FIN报文段即使不携带数据,也要消耗掉一个序号
- 发送后,A进入
FIN_WAIT1状态(终止等待1),等待B的确认 - 这是TCP连接释放的第一次挥手
- 服务器端收到连接释放报文段后即发出确认释放连接的报文段
- 该报文段,ACK=1,确认号为ack=u+1,自己的序号seq=v,该字号等于B前面己经传送过的数据的最后一个字节的序号加1
- 发送后,B进入
CLOSE_WAIT状态(关闭等待) - 此时服务器进程应该通知上层的应用进程,A到B这个方向的连接就释放了,这时TCP处于半关闭状态,即A己经没有数据要发了,但B若发送数据,A仍要接受,即从B到A这个方向的连接并没有关闭,这个状态可能会持续一些时间
- 这是TCP连接释放的第二次挥手
- A收到B的确认后,就进入了
FIN_WAIT2状态(终止等待2),等待B发出连接释放报文段 - 如果B已经没有要向A发送的数据了,其应用进程就通知TCP释放连接,发出连接释放报文段
- 该报文段,FIN=1,确认号ack=u+1,自己序号seq=w,因为在半关闭状态B可能又发送了一些数据,因此该序号为半关闭状态发送的数据的最后一个字节的序号加1
- 这时B进入
LAST-ACK状态(最后确认),等待A的确认 - 这是TCP连接释放的第三次挥手
- A收到B的连接释放请求后,必须发出确认报文段
- 该报文段,ACK=1,确认号ack=w+1,自己序号seq=u+1
- 发送后,进入
TIME_WAIT状态(时间等待) - 此时TCP连接还没有释放掉,必须经过时间等待计时器设置的时间2×MSL后,A才进入
CLOSED状态,MSL叫做最长报文寿命,RFC建议设为2分钟,因此A从进入TIWE_WAIT状态后,要经过4分钟才能进入CLOSED状态,而B只要收到了A的确认后,就进入了CLOSED状态 - 这是TCP连接释放的第四次挥手
- 连接两端双方都进入
CLOSED状态后,连接就完全释放了
双方主动关闭的TCP连接释放过程:

-
TCP传输连接关闭也可以由双方同时主动进行
-
当两端对应的网络应用层进程同时调用CLOSE原语,发送FIN数据段执行关闭命令时,两端均从ESTABLISHED状态转变为FIN_WAIT1状态
-
任意一方收到对端发来的FIN数据段后,其状态均由FIN_WAIT1转变到CLOSING状态,并发送最后的ACK数据段
-
当收到ACK数据段后,转变为TIME_WAIT状态,在等待2×MSL时间后进入到CLOSED状态
-
连接两端双方都进入CLOSED状态后,连接就完全释放了
为什么在TIME-WAIT状态后必须等待2×MSL时间呢?
- 为了保证A发送的最后一个ACK报文段能够到达B,该ACK报文段很有可能丢失,因而使B收不到对已发送的FIN_ACK报文段的确认
- 确认报文丢失,会引起B的重传,而A就在2×MSL时间内收到这个重传的FIN_ACK报文段,则A重传一次确认报文,重新启动2MSL计时器
- 如果A在TIME_WAIT状态不等待一段时间就直接释放连接,进入CLOSED状态,那么就无法收到B重传的FIN_ACK报文段,也就不会重传确认报文段,B就无法正常进入CLOSED状态
- 防止已失效的请求连接出现在本连接中,在连接处于2×MSL等待时,任何迟到的报文段将被丢弃,这样就可以使下一个新连接中不会出现旧连接的延迟报文段
补充:
-
执行主动关闭后会进入TIME_WAIT,并等待2×MSL的时间
-
对客户端来说,如果终止并立即重启一个客户程序,则新的客户程序将不再重用之前相同的本地端口,而是使用新的端口
-
但对于服务器来说,服务器总是用我们熟知的端口,因此在2×MSL时间内,重启服务器会出错
- 为了避免这个错误,服务器在2×MSL时间内,虽然可以重新启动,但是它还是要平静的等待2×MSL时间后才能进行下一次连接
更多推荐
所有评论(0)