TCP报文段首部格式
- 序列号seq: 占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;序列号seq就是这个报文段中的第一个字节的数据编号。
- 确认号ack: 占4个字节,期待收到对方下一个报文段的第一个数据字节的序号;序列号表示报文携带数据的第一个字节的编号;而确认号指的是期望收到下一个字节的编号;因此当前报文段最后一个自己的编号+1即为确认号。
- 确认ACK:占1位,仅当ACK=1时,确认号字段才有效。当ACK=0,确认号无效
- 同步SYN: 连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1,因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建立连接时才会被置1,握手完成后SYN标志位被置0。
- 终止FIN: 用来释放一个连接。FIN=1表示,此报文段的发送方的数据已经发送完毕,并要求释放运输连接。
TCP建立连接三次握手
(1)三次握手的过程
- 主机A向主机B发送TCP连接请求数据包,其中包含主机A的初始序列号seq(A)=x。(其中报文中同步标志位SYN=1,ACK=0,表示这是一个TCP连接请求数据报文;序号seq=x,表明传输数据时的第一个数据字节的序号是x);
- 主机B收到请求后,会发回连接确认数据包。(其中确认报文段中,标识位SYN=1,ACK=1,表示这是一个TCP连接相应数据报文,并含有主机B的初始序列号seq(B)=y,以及主机B对主机A初始序列号的确认号ack(B)=seq(A)+1=x+1)
-
第三次,主机A收到主机B的确认报文后,还需作出确认,即发送一个序列号seq(A)=x+1;确认号为ack(A)=y+1的报文。
(2)为什么需要第三次握手?
还要再发送一次确认是为了,防止已失效的连接请求报文段突然又传到了B,因而产生错误。
已失效的报文段:正常情况下:A发出连接请求,但因为丢失了,故而不能收到B的确认。于是A重新发出请求,然后收到确认,建立连接,数据传输完毕后,释放连接,A发了2个,一个丢掉,一个到达,没有“已失效的报文段”。
但是,某种情况下,A的第一个的某个节点滞留了,延误到达,本来这是一个早已失效的报文段,但是在A发送第二个,并且得到B的回应,建立了连接以后,这个报文段竟然到达了,于是B就认为,A又发送了一个新的请求,于是发送确认报文段,同意简历连接,假若没有三次的握手,那么这个连接就建立起来了(有一个请求和一个回应),此时,A收到B的确认,但A知道自己没有发送建立连接的请求,因此不会理睬B的这个确认,于是呢,A也不会发送任何数据,而B呢确认为新的连接建立了起来,一直等待A发送数据给自己,此时B的资源就被白白浪费了。于是采用三次握手的话,A就不发送确认,那么B由于收不到确认,也就知道没有必要建立连接。
简而言之:第三次握手,主机A发送一次确认是为了防止:如果客户端迟迟没有收到服务器返回的确认报文,这时它会放弃连接,重新启动一条连接请求;但问题是:服务器不知道客户端没收到,所以它会收到两个连接请求,白白浪费了一条连接开销。
TCP释放连接四次挥手
(1)四次挥手过程
假设主机A为客户端,主机B为服务器,其释放TCP连接的过程如下:
- 关闭客户端到服务器的连接:首先客户端A发送一个FIN,用来关闭客户到服务器的数据传送,然后等待服务器的确认。其中终止标志位FIN=1,序列号seq=u;
- 服务器收到这个FIN,它发回一个ACK,确认号ack收到的序号加1;
- 关闭服务器到客户端的连接:也就是发送一个FIN给客户端
-
客户端收到FIN后,并发回一个ACK报文确认,并将确认序号sql设置为收到序号加1
首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
主机A发送FIN后,进入终止等待状态,服务器B收到主机A连接释放报文段后,就立即给主机A发送确认,然后服务器B就进入close-wait状态,此时TCP服务器进程就通知高层应用进程,因而从A到B的连接就释放了。此时是“半关闭”状态。即A不可以发送给B,但是B可以发送给A。
此时,若B没有数据报要发送给A了,其应用进程就通知TCP释放连接,然后发送给A连接释放报文段,并等待确认。A发送确认后,进入time-wait,注意,此时TCP连接还没有释放掉,然后经过时间等待计时器设置的2MSL后,A才进入到close状态。
(2)为什么要等待2MSL呢?
MSL即Maximum Segment Lifetime,也就是最大报文生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。引用《TCP/IP详解》中的话:“它(MSL)是任何报文段被丢弃前在网络内的最长时间”。RFC 793中规定MSL为2min,实际应用中常用的是30s、1min和2min等。
TCP的TIME_WAIT状态需要等待2MSL,当TCP的一端发起主动关闭,在发出最后一个ACK包后,即3次挥手完成后发送了第四次挥手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间,等待2MSL时间主要目的是怕最后一个ACK包对方没收到,那么对方在超时后将重发第三次挥手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。在TIME_WAIT状态时两端的端口不能使用,要等到2MSL时间结束才可继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。不过在实际应用中可以通过设置SO_REUESADDR选项达到不必等待2MSL时间结束再使用此端口。
概括原因如下: - 为了保证A发送的最后一个ACK报文段能够到达B。即最后这个确认报文段很有可能丢失,那么B会超时重传,然后A再一次确认,同时启动2MSL计时器,如此下去。如果没有等待时间,发送完确认报文段就立即释放连接的话,B就无法重传了(连接已被释放,任何数据都不能重传了),因而也就收不到确认,就无法按步骤进入CLOSE状态,即必须受到确认才能close。
- 防止“已失效的连接请求报文段”出现在连接中。经过2MSL,那些在这个连接持续的时间内,产生的所有报文段就可以都从网络中消失。即在这个连接释放的过程中会有一些无效的报文段停滞在某个节点,但是呢,经过2MSL这些无效报文段就肯定可以发送到目的地,不会滞留在网络中。这可以看出:B结束TCP连接的时间比A早一点,因为B受到确认就断开连接了,而A还得等待2MSL。
(2)为什么TCP释放连接需要四次?
TCP建立连接需要进行三次握手,而断开连接要进行四次。这是由于TCP的半关闭造成的。因为TCP连接是全双工的(即数据可在两个方向上同时传递),所以进行关闭时每个方向上都要单独进行关闭。这个单方向的关闭就叫半关闭。当一方完成它的数据发送任务,就发送一个FIN来向另一方通告将要终止这个方向的连接。
注意: - 发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送),但是还可以接受数据。收到一个FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍然能发送数据,比如:如主机A收到主机B的FIN断开TCP连接请求,只是表示主机B已经发送完数据,主机A收到FIN后作出应答,并终止这个方向的数据传输,此时处于半关闭状态。但是主机A仍然可以发送数据,只有当主机A发送完数据后并发送FIN给主机B时,主机B才停止这个方向的数据传输,并关闭TCP连接。
- 在很多时候,TCP连接的断开都会有TCP层自动关闭。