前文中,我们介绍了UDP的简易结构与功能特点。现在鸟枪换大炮,来看看传输层的重头戏–TCP协议。
TCP协议在数据传输前需要建立会话,以供全双工通信,并具有可靠传输、流量控制、拥塞避免的功能。
在这一节中,我们将介绍TCP报文的首部格式以及其三次握手、四次挥手(连接管理)。
1.源端口/目的端口:与UDP中相同,各占2字节
2.序号、确认号:各占4字节,与TCP面向字节流的特点相关,TCP将需要发送的数据存放在TCP缓存中,依次发送。序号即使本次报文发送的第一个字节号,确认号即是期待接收字节流的第一个字节号。
3.数据偏移:TCP报文段的数据部分距离TCP报文段的起始段偏移量,即首部长度,单位为4B,占6字节
4.保留位:占6字节
5.六个标志位,分别占1字节:
1)URG:紧急标志位,UGG=1的报文段具有优先发送权
2)ACK:确认标志位,ACK=1的报文段中,确认号才有意义,在连接建立后,所有报文的ACK=1
3)*PSH:推送位
4)*RST:复位,TCP连接出现严重差错时,释放连接,重新建立新连接
5) SYN:同步位,SYN=1的报文段是一个连接请求\连接接受报文
6)FIN:中止位,FIN=1表明报文段发送数据完毕,请求释放连接
6.窗口:告诉对方自己的接收窗口大小,便于进行流量控制
7.检验和:检验算法与UDP相同,只是伪首部中的协议字段是6,而非17
8.紧急指针:URG=1时,指出本报文段中紧急数据的字节数
9.选项:最大报文长度MSS、窗口扩大、时间戳、选择确认等
10.填充:由于选项部分大小不定,填充部分将TCP首部补齐4字节整数倍
什么是连接管理?TCP是基于全双工的可靠传输,这一切的基础都建立在TCP在数据传输前需要建立连接之上。连接管理主要分为两部分,即建立连接的三次握手与释放连接的四次挥手。
第一次握手:
客户端向服务器发送SYN报文段,SYN=1,seq=x,客户端进入SYN-SENT状态
第二次握手:
服务器端收到SYN报文段后,为TCP连接分配缓存和变量,返回一个SYN报文段,SYN=1,ACK=1,seq=y,ack=x+1,服务器进入SYN-RECD状态
第三次握手:
客户端收到服务器发来的SYN报文后,为TCP连接分配缓存和变量,返回一个ACK报文,ACK=1,seq=x+1,ack=y+1,可携带数据,客户端连接建立成功,进入ESTABLISHED状态。
服务器收到ACK报文后,连接建立成功,进入ESTABLISED状态。
第一次挥手:
客户端发送一个FIN报文,请求关闭连接,FIN=1,seq=u,客户端进入FIN-WAIT-1状态
第二次挥手:
服务器接收到FIN报文,返回一个ACK报文,ACK=1,seq=v,ack=u+1,客户端进入CLOSE-WAIT状态。
此时,TCP连接处于半关闭状态–客户端不向服务器传输数据,但服务器仍可向客户端传送数据
第三次挥手:
服务器向客户端发送FIN报文,FIN=1,ACK=1,seq=w,ack=u+1,客户端进入LAST-ACK状态
第四次挥手:
客户端向服务器段发送ACK报文,ACK=1,seq=u+1,ack=w+1,等待2MSL后,进入CLOSED状态
服务器收到ACK报文后,进入CLOSED状态
在程序员面试中,三次握手与四次挥手是面试官老生常谈的话题,在此列举一些常见的问题:
问:三次握手可以改成两次握手吗?
答:不可以,容易产生死锁问题。若连接建立只使用两次握手,那么服务器在接收到SYN报文后,会认为连接已经建立成功,同时返回确认报文以及应用数据。若此时该确认报文丢失,则客户端认为连接建立失败,将忽略服务器发来的所有报文,等待确认报文。而傻傻的服务器向客户端发送报文,却得不到客户端的确认报文,同样进入等待状态。两者互相等待,形成死锁。
问:为什么三次握手中最后客户端需要发送一次确认报文
答:为了避免已失效的连接请求突然发送至服务器,造成产生错误。
现实中可能有这样一种情况:客户端给服务器发送的第一个SYN报文,由于网络原因,传输缓慢没有立即到达。于是客户端发送了第二个SYN报文,且迅速到达了服务器,成功建立连接,完成数据传输,连接释放。而后第一个迷路的SYN突然成功到达了服务器,服务器误以为客户端又一次发起了请求,便又建立了一次连接、传输数据等,这样便浪费了资源。
而若有了第三次确认,即使已失效的第一个SYN报文导致服务器返回了SYN报文,但客户端不会对这个报文进行确认,双方不会建立第二次连接。
问:为什么握手是三次,而挥手是四次?
答:第二次握手相当于将第二次挥手和第三次挥手合并成一次,故省去了一次
问:为什么第四次挥手后要等待2MSL?
答第四次挥手的ACK报文可能丢失,接收不到ACK报文的服务器会重发第三次挥手的SYN报文,处于2MSL等待期间的客户端若收到了该重发的SYN报文,则会重发第四次挥手的ACK报文,若未收到,则认为第四次挥手报文成功到达,连接关闭。MSL通常设置为75s
问:建立连接后,客户端突然出现故障怎么办?
答:TCP设有一个保活计时器,客户端如果出现故障,服务器不会一直等待下去。服务器每收到一次客户端的请求后都会重新复位该定时器,时间设置为2小时,若两小时内没收到客户端的数据,服务器便发送一个探测报文,并每隔75s发送一次。若连续10个探测报文都得不到回应,服务器判定客户端出现故障,直接关闭连接