写在前面,本来觉得关于TCP的建立与终止是一些纯理论的东西,看UNIX网络编程时大概看了一下,没想到后面所涉及到的编程都需要十分熟悉TCP的建立、连接、终止的过程细节,所以特地返回来总结一下。
1、TCP连接建立与终止
关于SYN(同步)、ACK(确认)、FIN(结束)的值得说明:客户机的初始序列号为J,服务器的初始序列号为K。SYN中的ACK的确认号是发送该ACK的这端所期待的下一个序列号。因为SYN占据一个字节的序列号空间,所以每一个SYN的ACK中的确认号就是该SYN的初始序列号+1。同样地,每一个FIN的的ACK中的确认号为该FIN的序列号+1。
(1)“三路握手”建立连接
A.服务器(通常通过调用socket、bind、listen来完成——“被动连接”)必须准备接受外来的连接。;
B.客户机(通过调用connect)发起“主动打开”。这将会导致客户TCP发送一个SYN(同步)分节——告诉服务器客户机将在该连接中发送的数据的初始序列号。
C.服务器必须确认(ACK)客户机的SYN(同步),同时服务器自己也发送依个SYN(同步)分节——告诉客户机服务器将在同一连接中发送的数据的初始序列号。(服务器在单个分节中发送SYN和对客户的ACK)
D.客户机也必须确认服务器的SYN。
(2)“四路握手”终止连接——每个方向都有一个FIN和ACK,因此需要四路。
A.某个应用进程(通过调用close)发起“主动关闭”,同时该端的TCP会发送一个FIN(结束)分节——表示数据发送完毕。
B.接收到该FIN的对端执行“被动关闭”,并发送ACK确认该FIN。——因为FIN的接收意味着接收端进程在相应连接上再无额外数据可接收,将作为一个文件结束符(EOF)放在已排队等候该应用进程接收的任何其他数据之后。
C.一段时间后,接收到这个文件结束符(EOF——FIN)的进程将调用close关闭它的套接字,并同时也发送一个FIN。
D.接收这个最终FIN的原发送端TCP(执行主动关闭的一端)确认(ACK)这个FIN。
特别提醒:
在上述步骤B和C之间,从执行“主动关闭”一端到执行“被动关闭”一端流动数据是可能的(不发送数据了,并不是说也不接收了),这称为“半关闭”。
一个Unix进程无论自愿还是非自愿终止时,所有打开的描述符都被关闭,这就导致仍然打开的任何TCP连接上也发出一个FIN。
无论是客户机还是服务器,任何一端都可以执行主动关闭。(通常由客户机执行主动关闭)
(3)TCP状态转换图
TCP为一个连接定义了11种状态。
CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认
SYN_SENT:应用已经开始,打开一个连接
ESTABLISHED:正常数据传输状态
FIN_WAIT1:应用说它已经完成
FIN_WAIT2:另一边已同意释放
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉
(4)关于TIME_WAIT的详细说明:
执行主动关闭的那端经历TIME_WAIT这个状态,该端停留在这个状态的的持续时间是最长分节生命期的两倍,如上图的2MSL。(MSL是任何IP数据报能够给在因特网中存活的最长时间)
客户 服务器
TIME_WAIT状态存在的两个理由:
A.可靠地实现TCP全双工连接的终止;
如上图,如果TCP打算执行全双工关闭(彻底终止某个连接上两个方向的数据流),那么它必须正确处理终止序列4个分节的任何一个分节丢失的情况。现在我们假设最终的ACK丢失。则服务器将重新发送它的最终那个FIN,因此客户必须维护状态信息,以允许它重新发送最终那个ACK。要是客户不维护状态信息,它将响应一个RST(重置连接、复位连接),这将被服务器解释成一个错误。
(TCP协议在关闭连接的四次握手过程中,最终的ACK是由主动关闭连接的一端(后面统称A端)发出的,如果这个ACK丢失,对方(后面统称B端)将重发出最终的FIN,因此A端必须维护状态信息(TIME_WAIT)允许它重发最终的ACK。如果A端不维持TIME_WAIT状态,而是处于CLOSED 状态,那么A端将响应RST分节,B端收到后将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。
因而,要实现TCP全双工连接的正常终止,必须处理终止过程中四个分节任何一个分节的丢失情况,主动关闭连接的A端必须维持TIME_WAIT状态 。)
B.允许老的(迷途的)重复分节在网络中消逝;
TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个迟到的迷途分节到达时可能会引起问题。在关闭“前一个连接”之后,马上又重新建立起一个相同的IP和端口之间的“新连接”,“前一个连接”的迷途重复分组在“前一个连接”终止后到达,而被“新连接”收到了。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。
2、基本TCP客户/服务器套接字函数