建立TCP的连接,就得先说说TCP的报文首部。TCP的全部功能都体现在它首部的各个字段的作用。下面说明TCP首部格式。
TCP报文段的前20个字节是固定的,后面有4n个字节是根据需要而增加的选项(n是整数)。因此TCP首部的最小长度是20个字节。
TCP报文首部
首部固定部分各字段的意义如下:
(1)源端口和目的端口 各占2个字节,分别写入 源端口号和目的端口号。
(2)序号 占4字节。序号范围是[0,2^32 -1],共232个序号。序号增加到232 -1后,下一个序号就又回到0。
例如:一报文段的序号字段值是301,而携带的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是301,最后一个字节的序号是400。显然,下一个报文段(如果还有的话)的数据序号应当从401开始。
(3)确认号 占4字节,是期望收到对方下一个报文段的第一个数据字节的序号。
例如:B正确收到了A发送过来的一个报文段,其序号字段值是501,而数据长度是200字节(序号501~700),这就表明B正确收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701。
总之,应当记住:
若确认号=N,则表明:到序号N-1为止的所有数据都已正确收到。
(4)数据偏移 占4位,是TCP报文段的首部长度。
(5)保留 占6位,保留为今后使用,但目前应置为0。
(6)紧急URG 当URG=1时,表明紧急指针字段有效。
(7)确认ACK 仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
(8)推送PSH 当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1。
(9)复位RST 当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接。
(10)同步SYN 在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1。
(11)终止FIN 用来释放一个连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放。
(12)窗口 占2字节。是接收方告诉发送方我还可以容纳窗口大小的数据。
例如:发送一个报文段,其确认号是701,窗口大小是1000。这就告诉对方:“从701号算起,我的接收缓存还可以接收1000个字节数据,你发送数据时,必须要考虑到这一点”。
(13)校验和 占2字节。校验首部和数据这两部分。
(14)紧急指针 占2字节。指出本报文段中的紧急数据的字节数。
(15)选项 长度可变,定义一些其他的可选的参数。当没有使用“选项”时,TCP的首部长度是20个字节。
TCP的连接建立
假定主机A运行的是TCP客户程序,而B运行TCP服务器程序。最初两端的TCP进程都处于CLOSED(关闭)状态。客户端A主动打开,而B被动打开。
1)服务器B先创建传输控制块TCB,准备接受客户的连接请求。然后服务器就处于LISTEN(收听)状态,等待客户的连接请求。如有,即作出相应。
2)客户端A也首先创建传输控制模块TCB。然后,向B发出连接请求报文段,这时报文首部中的同步位SYN=1,同时选择一个初始序列号 seq=x 。TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
3)B收到请求报文后,如同意建立连接,则向A发出确认。在确认报文中应该把 ACK位和SYN位都置1,确认号是ack=x+1,同时也要为自己选择一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。注意,这个报文也不能携带数据,但是同样要消耗一个序号。
4)TCP客户进程收到B的确认后,还要向B给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1。此时,TCP连接已经建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
5)当B收到A的确认后,也进入ESTABLISHED状态。
为什么A最后还要发送一次确认呢?
这主要是为了防止已失效的连接请求报文段突然又传送到B,因而产生错误。
所谓“已失效的连接请求报文段”是这样产生的。假设有这样一种场景,客户端发送了第一个请求连接并且没
有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器
没有收到,此时重新向服务器发送请求报文,此后客户端和服务器建立连接,传输数据,然后关闭连接。
此时之前滞留的那一次请求连接,网络通畅了到达了服务器,这个请求报文本该是失效的,但是,服务器
收到此失效请求报文,就误以为是客户端又发出一次新的连接请求。于是就向客户端发出确认报文,同意
建立连接。假定不采用报文握手,那么只要服务器发出确认,新的连接就建立了。
由于客户端没有发出建立连接请求,因此不会理睬服务器的确认,也不会向服务器发数据,但服务器却以
为连接已经建立了,等待客户端发数据,服务器的许多资源就这样白白浪费了。
TCP的连接释放
A和B都处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。
1)A的应用进程先向其TCP发出连接释放报文,并停止发送数据,主动关闭TCP连接。A把连接释放的报文段首部的终止控制位FIN置1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1)。此时,A进入FIN-WAIT-1(终止等待1)状态,等待B的确认。注意, TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2)B收到连接释放报文后发出确认,ACK=1,ack=u+1,并且带上自己的序列号seq=v。此时,B就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态可能要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3)A收到B的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待B发出连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4)若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使用FIN=1,ack=u+1,由于在半关闭状态,B很可能又发送了一些数据,假定此时的序列号为seq=w,此时,B就进入了LAST-ACK(最后确认)状态,等待A的确认。
5)A收到B的连接释放报文段后,必须对此发出确认,在确认报文段中将ACK=1,ack=w+1,而自己的序列号是seq=u+1。此时,A就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命,建议设为2分钟,对于现在的网络,MSL=2分钟可能长了些)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6)B只要收到了A发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
为什么A在TIME-WAIT状态必须等待2MSL的时间呢?
1)为了保证A发送的最后一个ACK报文段能够到达B。这个ACK报文段可能会丢失,因而使处在LAST-ACK(最后确认状态)的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A在2MSL时间内就能收到这个重传的报文段,最后,A和B就能正常释放连接。
2)防止“已失效的连接请求报文段”出现在本连接中。A在发送完最后一个ACK报文段后,再经过2MSL时间,就可以使本连接持续的时间内产生的所有报文段都从网络中消失。
连接建立后,A突然出现故障,此时应该怎么办?
除了时间等待计时器外,TCP还有一个保活计时器。当B每收到一次A发送来得数据时,就重新设置保活计时器,时间的设置通常是两个小时。若两个小时没有收到A的数据,B就发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文段后扔没有收到A的响应,B就认为A出现故障,接着就关闭连接。