所谓的“三次握手”:为了对每次发送的数据量进行跟踪与协商,确保数据段的发送和接收同步,根据所接收到的数据量而确认数据发送、接收完毕后何时撤销脸联系,并建立虚连接。
为了建立连接TCP连接,通信双方必须从对方了解如下信息:
1、对方报文发送的开始序号。
2、对方发送数据的缓冲区大小。
3、能被接收的最大报文段长度MSS。
4、被支持的TCP选项。
所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息.在socket编程中,客户端执行connect()时。将触发三次握手。
第一次
第一次握手:建立连接时,客户端发送SYN(seq=x)到服务器端,客户端进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号。
第二次
第二次握手:服务器收到SYN包,必须确认客户端的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态。
第三次
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据内容。
tcp的客户端和服务端需要进行三次握手才能建立连接。但是在三次握手过程中由于路由等问题导致请求连接的数据包遗失,我们知道tcp可以通过超时重传的机制对遗失数据包经行重新发送,那是怎样的一个过程呢?
第一次握手:我们知道第一次客户端发送申请连接数据包,会启动一个定时器,在定时器置零后客户端还未接收到服务端的回包将会重新发送一次申请,直到服务端发送回包。
第二次握手:第二次握手由服务端发送申请连接和确认包,当这个数据包遗失后,还应该是服务器发送重新发送吗?答案是不会,这跟客户端的状态有关,客户端发送第一次握手的时候,客户端将closed状态转换为SYN_SENT状态,由于服务端的回包并没有被接受,客户端的连接状态并不会改变,客户端还是认为自己所发送的第一次握手遗失。所以当第二次握手的数据包遗失后,还是由客户端重新发送第一次连接数据包。
第三次握手:客户端接收到第二次握手的回包后将会发送第三次握手确认包,此时客户端的状态为已连接状态,此时客户端认为已经和服务端建立连接,会产生数据的交互,当客户端发现与服务端之间的数据总会丢失和没有回应时,会向服务端发一个RST(报文段的RST标记号为1),强制服务端关闭连接。在重新建立连接。
总结:tcp连接重传机制与客户端和服务器端的状态有着紧密的联系相关。挥手和握手的过程同理。
首先,为什么是三次握手而不是四次或者更多?这个问题是比较简单的,因为既然三次能够解决的问题,为什么非要用四次来浪费资源?
但其实问题的重点在于,为什么不能只用两次?第三次握手去掉不行吗?
总的来说,三次握手是为了防止当已失效的连接请求报文段突然又传到服务端,造成双方的不一致,导致资源的浪费。
“已失效的连接请求报文段”指的是这样的情况,客户端发出一个SYN报文段,由于阻塞或者其他原因在网络中滞留,以至于客户端认为丢包了(其实并没有丢),于是重新发出一个SYN报文段,假设这一次顺利完成了,那么双方建立连接。这看起来似乎没什么问题,但网络中有一个隐患,就是那个还在网络中传输的SYN报文段,如果这个SYN在连接期间被服务端收到了,那服务端只会无视它,这样就万事大吉了,但如果是在连接释放之后被收到呢?此时服务端认为有人向他发出连接请求,于是响应一个SYNACK回去,如果采用两次握手的话,那么服务器认为此时连接已经建立好了。但是当客户端收到这个SYNACK时,如果他并没有发起连接,那么他不会理睬这个SYNACK,就当没事发生过(如果客户端此时正好发起连接,那其实他也不会理睬这个SYNACK,因为确认号不对啊。)。那问题就大了,这时候服务器以为连接好了,向客户端发送数据,而客户端处于CLOSED状态,会丢弃这些包,这样就很浪费了。并且还有一个尴尬的问题,就是这个时候当客户端打算发起连接时,服务端又不理睬了,在这里尬这,他们就别想互发数据了。当然这些问题似乎不是不可解决的,当客户端发现服务端老是向自己发数据,而自己总是丢弃,可能会向服务端发一个RST(报文段的RST标记号为1),强制服务端关闭连接。但资源总归是浪费了一会了。而用三次握手就不会出现这样的问题。
TCP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。
第一步
当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段。
第二步
主机B收到这个FIN报文段之后,并不立即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知自己相应的应用程序:对方要求关闭连接(先发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。
第三步
主机B的应用程序告诉TCP:我要彻底的关闭连接,TCP向主机A送一个FIN报文段。
第四步
主机A收到这个FIN报文段后,向主机B发送一个ACK表示连接彻底释放。
因为当服务端收到客户端的 SYN 连接请求报文后,可以直接发送 SYN+ACK 报文。其中 ACK 报文是用来应答的,SYN 报文是用来同步的。但是关闭连接时,当服务端收到 FIN 报文时,很可能并不会立即关闭 SOCKET,所以只能先回复一个 ACK 报文,告诉客户端,“你发的 FIN 报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送 FIN 报文,因此不能一起发送。故需要四次挥手。