tcp为什要三次握手

准备知识: 

单工:信息只能单向传递。发送-->接收,单向,不能返回响应。

双工:指的是信息可双向发送。

全双工:信息可同时双向传递。

半双工:不能同时,单行道,一边传输完了,另一边才能发起传输。

 

由于IP协议是不可靠的,为了在不可靠信道上传输可靠数据,就要进行三次握手,准确的说是发送三次预备信息,这样就完成了信息传递的双工准备。(HTTP协议是需要双向传递的)。

双方都需要确认自己的发信和收信功能正常,收信功能通过接收对方信息得到确认,发信功能需要发出信息—>对方回复信息得到确认。

第一次发信息: CLIENT --> SERVER 

第二次发信息:  SERVER --> CLIENT     客户端接收到了,确信自己可以收,并且收到的信息里有上一步发过去信息里的内容,说明也是可以发送的。

第三次发信息: CLIENT --> SERVER     同理,服务端也确信了可以收和发的能力。

 

如果没有第三次发信息,只是保证了一个单向的连接畅通。客户端可以大胆地单向给服务端发信息。服务端由于没收到过“回音”,不能确定对方能不能收到,tcp协议是保证了传输可靠性的,不允许这种不确定性存在,就不允许服务器给客户端发送响应了。

当然UDP协议就不保证输出的可靠性了,就算没有收到过“回音”也敢发出信息。

 

-------------------------------连接断开的时候为什么要四次呢-----------------

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。

因为可以同时双向传输,A B 两个终端上有两个通道四个端口。分别是A端的读端口,A端的写端口和B端的读端,B端的写端口。 

TCP采用四次挥手关闭连接如图2所示。

以客户机发起关闭连接为例:
1.Client发送FIN给Server,Server收到Fin信息后知道已经没有要再接收的东西了,就关闭自己的读端口,然后发送ACK给CLient   --Server 读端关闭 
2.Client收到ACK后,知道对面已经关闭读通道了,现在自己可以把写端口关闭了                                                                    --Client  写端关闭
3.Server发送FIN给Client表示自己没有要发送的了,Client收到FIN后,关闭自己的读端口,然后发送ACK给Server                  --Client  读端关闭
4.Server接受到ACK后,关闭自己的写端口,并且Server的Conection状态变为Closed,彻底关闭可以重用了。                        --Server 写端关闭 
5.Client在最后发给Server的ACK报文后等待两个MSL时间,然后Client端的onection状态变为Closed。

* 可以看出,读端口的关闭,是收到对方发来的FIN信号即可。而写端口的关闭是,发送一个FIN给对方,收到对方的ACK回复才可以关闭。这是确保数据包都发送过去的重发机制要求的。

第2步和第3步很多时候都可以合并,就是收到对方的FIN的时候,自己没有要发过去的东西,就把ACK和FIN一起返回去了。

* 主动发起关闭连接的一方发出最后的ACK报文后达到TIME_WAIT状态,而且这个状态要保持Maximum Segment Lifetime的两倍时间。这是因为两点:

保证TCP协议的全双工连接能够可靠关闭
2 保证这次连接的重复数据段从网络中消失

先说第一点,如果Client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。这种情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候再发一个ACK给对方。保证对方收到,最后正确的关闭连接。(MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间,为操作系统决定大小,windows默认MSL值为2分钟。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。)

再说第二点,如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。

------------------TCP的粗暴关闭连接方式--------------------

除了如上可靠的关闭方式之外,TCP还提供了另外一种不可靠的关闭方式RST(Reset)
(CLOSED)  A    ---RST-->  B (CLOSED)
  A端发送RST状态之后,TCP进入CLOSED状态,B端接收到RST后,也即可进入CLOSED状态。
在上面第一关闭方式上(可靠的),非常遗憾,A端在最后发送一个ACK请求后,并不能马上将该Socket回收,因为A并不能确定B一定能够接收到这个ACK报文,因此A端必须对这个Socket维持TIME_WAIT状态2MSL。

如果A端是Client,这并不会成为问题,但如果A端是Server,那就很危险了,如果连接的Socket非常多,而又维持如此多的TIME_WAIT状态的话,那么有可能会将Socket耗尽(报Too Many Open File)。

服务端为了解决这个问题,可选择的方式有三种:
    Ø  保证由客户端主动发起关闭(即做为B端)
    Ø  关闭的时候使用RST的方式
    Ø  对处于TIME_WAIT状态的TCP允许重用
一般我们当然最好是选择第一种方式,实在没有办法的时候,我们可以使用SOCKET参数【SO_LINGER】开启第二种方式,使用SOCKET参数【SO_REUSEADDR】开启第三种方式

posted @ 2016-01-30 22:38 寂静沙滩 阅读(...) 评论(...) 编辑 收藏