目的

为了对每次发送的数据量进行跟踪与协商,确保数据段的发送和接收同步,根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系,并建立虚连接。


基础名词

SYN

同步序列编号(Synchronize Sequence Numbers)。是TCP/IP建立连接时使用的握手信号。是TCP连接的第一个包,非常小的一种数据包。SYN 攻击包括大量此类的包,由于这些包看上去来自实际不存在的站点,因此无法有效进行处理。每个机器的欺骗包都要花几秒钟进行尝试方可放弃提供正常响应。

ACK

确认字符 (Acknowledge character),在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。在TCP/IP协议中中,如果接收方成功的接收到数据,那么会回复一个ACK数据。通常ACK信号有自己固定的格式,长度大小,由接收方回复给发送方。

seq

是数据包本身的序列号

ack

是对收到的数据包的确认,值是下次希望接收的数据包的序列号。

seq和ack

在第一次消息发送中,A随机选取一个序列号作为自己的初始序号(seq=x)发送给B;第二次消息B使用ack对A的数据包进行确认,因为已经收到了序列号为x的数据包,正准备接收序列号为x+1的包,所以ack=x+1,同时B告诉A自己的初始序列号,就是seq=y;第三条消息A告诉B收到了B的确认消息并准备建立连接,A自己此条消息的序列号是x+1,所以seq=x+1,而ack=y+1是表示A正准备接收B序列号为y+1的数据包。

seq和ack详细解释

https://www.jianshu.com/p/15754b4e9458


简单流程

客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,最后客户机再以ACK消息响应。这样就可以在客户机和服务器之间建立起可靠的TCP连接,数据就可以在客户机和服务器之间传递。


具体步骤

第一步,Client会进入SYN_SENT状态,并发送Syn 消息给Server端,SYN标志位在此场景下被设置为1,同时会带上Client这端分配好的Seq号,这个序列号是一个U32的整型数,该数值的分配是根据时间产生的一个随机值,通常情况下每间隔4ms会加1。除此之外还会带一个MSS,也就是最大报文段长度,表示Tcp传往另一端的最大数据块的长度。

第二步,Server端在收到,Syn消息之后,会进入SYN_RCVD状态,同时返回Ack消息给Client,用来通知Client,Server端已经收到SYN消息并通过了确认。这一步Server端包含两部分内容,一部分是回复Client的Syn消息,其中ACK=1,Seq号设置为Client的Syn消息的Seq数值+1;另一部分是主动发送Sever端的Syn消息给Client,Seq号码是Server端上面对应的序列号,当然Syn标志位也会设置成1,MSS表示的是Server这一端的最大数据块长度。

第三步,Client在收到第二步消息之后,首先会将Client端的状态从SYN_SENT变换成ESTABLISHED,此时Client发消息给Server端,这个方向的通道已经建立成功,Client可以发送消息给Server端了,Server端也可以成功收到这些消息。其次,Client端需要回复ACK消息给Server端,消息包含ACK状态被设置为1,Seq号码被设置成Server端的序列号+1。(备注:这一步往往会与Client主动发起的数据消息,合并到一起发送给Server端。)

第四步,Server端在收到这个Ack消息之后,会进入ESTABLISHED状态,到此时刻Server发向Client的通道连接建立成功,Server可以发送数据给Client,


image.png


重要概念

未连接队列

在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(seq=j)开设一个条目,该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于 SYN_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。

Backlog参数

表示内核为相应套接字排队的最大连接个数。SYN-ACK重传次数服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同。

半连接存活时间

半连接队列的条目存活的最长时间,也即服务器从收到SYN包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和。有时我们也称半连接存活时间为Timeout时间、SYN_RECV存活时间


常见问题

为什么要握三次,两次不行吗?

首先非常明确的是两次握手是最基本的。

第一次握手,客户端发了个连接请求消息到服务端,服务端收到信息后知道自己与客户端是可以连接成功的,但此时客户端并不知道服务端是否已经接收到了它的请求,

第二次握手服务端接收到消息后的应答,客户端得到服务端的反馈后,才确定自己与服务端是可以连接上的,

客户端只有确定了自己能与服务端连接上才能开始发数据。所以两次握手肯定是最基本的。

第三次握手是为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误。譬如发起请求遇到类似这样的情况:客户端发出去的第一个连接请求由于某些原因在网络节点中滞留了导致延迟,直到连接释放的某个时间点才到达服务端,这是一个早已失效的报文,但是此时服务端仍然认为这是客户端的建立连接请求第一次握手,于是服务端回应了客户端,第二次握手。如果只有两次握手,那么到这里,连接就建立了,但是此时客户端并没有任何数据要发送,而服务端还在傻傻的等候佳音,造成很大的资源浪费。所以需要第三次握手,只有客户端再次回应一下,就可以避免这种情况。


为什么连接的时候是三次握手,关闭的时候却是四次握手?

因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。


如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。


(晚上画动态图)

参考链接

https://blog.csdn.net/qq_38950316/java/article/details/81087809

https://www.jianshu.com/p/cd379b79b9be

百度百科