首页 > 代码库 > 三连四断
三连四断
1.TCP连接的建立 (1)首先是服务器初始化的过程,从CLOSED(关闭)状态开始通过顺序调用SOCKET、BIND、LISTEN和ACCEPT原语创建Socket套接字,进入LISTEN(监听)状态,等待客户端的TCP传输连接请求。
(2)客户端最开始也是从CLOSED状态开始调用SOCKET原语创建新的Socket套接字,然后在需要再调用CONNECT原语,向服务器发送一个将SYN字段置1(表示此为同步数据段)的数据段(假设初始序号为i),主动打开端口,进入到SYNSENT(已发送连接请求,等待对方确认)状态。
TCP传输连接建立的三次握手过程
(3)服务器在收到来自客户端的SYN数据段后,发回一个SYN字段置1(表示此为同步数据段),ACK字段置1(表示此为确认数据段),ack(确认号)=i+1的应答数据段(假设初始序号为j),被动打开端口,进入到SYN RCVD(已收到一个连接请求,但未进行确认)状态。这里要注意的是确认号是i+1,而不是i,表示服务器希望接收的下一下数据段序号为i+1。
(4)客户端在收到来自服务器的SYN+ACK数据段后,向服务器发送一个ACK=1(表示此为确认数据段),序号为i+1,ack=j+1的确认数据段,同时进入ESTABLISHED(连接建立)状态,建立单向连接。要注意的是,此时序号为i+1,确认号为j+1,表示客户端希望收到服务器的下一个数据段的序号j+1。
(5)服务器在收到客户端的ACK数据段后,进入ESTABLISHED状态,完成双向连接的建立。
双方同时主动连接的TCP连接建立过程
正常情况下,传输连接都是由一方主动发起的,但也有可能双方同时主动发起连接,此时就会发生连接碰撞,最终只有一个连接能够建立起来。因为所有连接都是由它们的端点进行标识的。如果第一个连接请求建立起一个由套接字(x,y)标识的连接,而第二个连接也建立了这样一个连接,那么在TCP实体内部只有一个套接字表项。
当出现同时发出连接请求时,则两端几乎在同时发送一个SYN字段置1的数据段,并进入SYN_SENT状态。当每一端收到SYN数据段时,状态变为SYN_RCVD,同时它们都再发送SYN字段置1,ACK字段置1的数据段,对收到的SYN数据段进行确认。当双方都收到对方的SYN+ACK数据段后,便都进入ESTABLISHED状态。图10-39显示了这种同时发起连接的连接过程,但最终建立的是一个TCP连接,而不是两个,这点要特别注意。
图 10-39 同时发起连接的TCP连接建立流程
从图中可以看出,一个双方同时打开的传输连接需要交换4数据段,比正常的传输连接建立所进行的三次握手多交换一个数据段。此外要注意的是,此时我们没有将任何一端称为客户或服务器,因为每一端既是客户又是服务器。
2. 双方主动关闭的TCP连接释放流程
与可以双方同时建立TCP传输连接一样,TCP传输连接关闭也可以由双方同时主动进行(正常情况下都是由一方发送第一个FIN数据段进行主动连接关闭,另一方被动接受连接关闭),如图10-41所示。具体描述如下:
同时主动关闭TCP连接的流程
- 三次握手建立连接时,发送方再次发送确认的必要性
- 主要是为了防止已失效的连接请求报文段突然又传到了B,因而产生错误。假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,一直延迟到连接释放以后的某个时间才到达B,本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求,于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了,这样一直等待A发来数据,B的许多资源就这样白白浪费了。
- 三次握手是连接两端正确同步的充要条件,因为TCP建立在不可靠的分组传输服务之上,报文可能丢失、延迟、重复和乱序,因此协议必须使用超时和重传机制。如果重传的连接请求和原先的连接请求在连接正在建立时到达,或者当一个连接已经建立、使用和结束之后,某个延迟的连接请求才到达,就会出现问题。采用三次握手协议就可以解决这些问题。如客户端发送的ACK数据段就是为了避免因网络延迟而导致的重复连接,因为这时客户端就可通过检查ACK数据段中的确认号就可得知该连接请求是否已失效。
- 四次挥手释放连接时,等待2MSL的意义
1)可靠地实现TCP全双工连接的终止 第一,为了保证A发送的最有一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN和ACK报文段的确认。B会超时重传这个FIN和ACK报文段,而A就能在2MSL时间内收到这个重传的ACK+FIN报文段。接着A重传一次确认。在进行关闭连接四路握手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,因此客户端必须维护状态信息允 许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节,服务器将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭 的客户端必须维持状态信息进入TIME_WAIT状态。
TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个 原来的迷途分节就称为lost duplicate。在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身 (incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。为了避免这个情 况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时 候,来自连接先前化身的重复分组已经在网络中消逝。
短时间的多次请求可能会导致服务器上存在很多的TIME_WAIT,那么导致端口被用尽,无法继续访问应用程序。四次握手关闭也不是必须的,也可以通过套接字选项设置不需要关注TIME_WAIT状态。
状 态 | 描 述 |
CLOSED | 关闭状态,没有连接活动或正在进行 |
LISTEN | 监听状态,服务器正在等待连接进入 |
SYN RCVD | 收到一个连接请求,尚未确认 |
SYN SENT | 已经发出连接请求,等待确认 |
ESTABLISHED | 连接建立,正常数据传输状态 |
FIN WAIT 1 | (主动关闭)已经发送关闭请求,等待确认 |
FIN WAIT 2 | (主动关闭)收到对方关闭确认,等待对方关闭请求 |
TIMED WAIT | 完成双向关闭,等待所有分组死掉 |
CLOSING | 双方同时尝试关闭,等待对方确认 |
CLOSE WAIT | (被动关闭)收到对方关闭请求,已经确认 |
LAST ACK | (被动关闭)等待最后一个关闭确认,并等待所有分组死掉 |
TCP建立与释放的变迁如图所示:
- 客户进程变迁的过程(粗实线)
- 连接建立:设一个主机的客户进程发起连接请求(主动打开),这时本地TCP实体就创建传输控制快(TCB),发送一个SYN为1的报文,进入SYN_SENT状态。当收到来自进程的SYN和ACK时,TCP就发送出三次握手中的最后一个ACK,进而进入连接已经建立的状态ESTABLISHED。
- 连接释放:设运行客户进程主机本地TCP实体发送一个FIN置为1的报文,等待着确认ACK的到达,此时状态变为FIN_WAIT_1。当运行客户进程主机收到确认ACK时,则一个方向的连接已经关闭。状态变成FIN_WAIT_2。当运行客户进程的主机收到运行服务器进程的主机发送的FIN置为1的报文后,应响应确认ACK时,这是另一个连接关闭。但此时TCP还要等待一段时间后才删除原来建立的连接记录。返回到初始的CLOSED状态,这是为了保证原来连接上的所有分组都从网络中消失了。
- 服务器进程变迁的过程(粗虚线)
- 连接建立:服务器进程发出被动打开,进入监听状态LISTEN。当收到SYN置为1的连接请求报文后,发送确认ACK,并且报文中的SYN也置为1,然后进入SYN_RCVD状态。在收到三次握手最后一个确认ACK时,就转为ESTABLISHED状态。
- 连接释放:当客户进程的数据已经传送完毕。就发出FIN置为1的报文给服务器进程,进入CLOSE_WAIT状态。服务器进程发送FIN报文段给客户进程,状态变为LAST_ACK状态。当收到客户进程的ACK时,服务器进程就释放连接。删除连接记录。回到原来的CLOSED状态
三连四断