TCP协议基础
TCP报文结构
TCP虽然是面向字节流的,但TCP传送的数据单元却是报文段。一个TCP报文段分为首部和数据两部分,而TCP的全部功能都体现在它首部中各字段的作用,结构如下图:
TCP报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的选项(n为整数)。因此TCP首部的最小长度是20字节。
首部固定部分各字段的意义如下:
(1)源端口和目的端口:各占两个字节,分别写入源端口号和目的端口号。和UDP的分用相似,TCP的分用功能也是通过端口实现。
(2)序号 seq:占4字节。序号范围是[0,232-1],共232(即4 294 967 296)个序号。序号使用mod 232计算,增加到232-1后再回到0。在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。首部中的序号字段值则指的是本报文段所发送的数据的第一个字节的序号。这个字段的名称也叫做“报文段序号”。
(3)确认号 ack:占4字节。是期望收到对方下一个报文段的第一个数据字节的序号。若确认号=N,则表明到序号N-1为止的所有数据都已正确收到。由于序号字段有32位长,可对4GB(即4千兆字节)的数据进行编号。在一般情况下可保证当序号重复使用时,旧序号的数据早已通过网络到达终点了。
(4)数据偏移:占4字节。该字段指出TCP报文段的首部长度,即数据起始处距离TCP报文段的起始处有多远。注意,“数据偏移”的单位是32位字(即以4字节长的字为计算单位)。由于4位二进制数能够表示的最大十进制数字是15,因此数据偏移的最大值为60字节,这 也是TCP首部的最大长度。
(5)保留:占6位,保留为今后使用,但目前应置为0。
之后为6个控制位,用来说明报文段的性质,意义如下:
(6)紧急 URG(URGent):当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快发送(高优先级数据)。需与首部中的紧急指针字段配合使用。
(7)确认 ACK(ACKnowledgment):仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
(8)推送 PSH(PuSH):当两个应用进程进行交互式的通信时,有时在一段的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。当发送方把PSH置1时,接收方收到报文段后会尽快交付应用进程。
(9)复位 RST(ReSeT):当RST=1时,表明TCP连接中出现严重差错,必须释放连接,再重新建立运输连接。RST置1还用来拒绝一个非法的报文段或拒绝打开一个连接。RST也可称为重建位或重置位。
(10)同步 SYN(SYNchronization):在建立连接时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文段中使SYN=1且ACK=1。因此,SYN置为1表示一个连接请求或连接接收报文。
(11)终止 FIN(FINis):用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已经发送完毕,并要求实放运输连接。
(12)窗口:占2字节。窗口值是[0,2^16-1]之间的整数。窗口指的时发送本报文段的一方的接收窗口。窗口值告诉对方:从本报文段首部中的确认好算起,接收方目前允许对方发送的数据量(单位为字节)。窗口值作为接收方让发送方设置其发送窗口的依据。窗口字段指出了现在允许对方发送的数据量。窗口值经常在动态变化。
(13)检验和:占2字节。检验的范围包括首部和数据两部分。和UDP用户数据报一样,计算时要在TCP报文段的前面加上12字节的伪首部。伪首部的格式与UDP的伪首部一样,但需把伪首部第4个字段中的17改为6(TCP的协议号是6),把第5字段中的UDP长度改为TCP长度。接收方收到此报文段后,仍要加上这个伪首部来计算检验和。若使用IPv6,则相应的伪首部也要改变。
(14)紧急指针(Urgent Pointer):占2字节。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数。因此紧急指针指出了紧急数据的末尾在报文段中的位置。即使窗口为0时,也可发送紧急数据。
(15)选项:长度可变,最长可达40字节。当没有使用选项时,TCP的首部长度时20字节。
MSS(Maximum Segment Size)最大报文段长度:MSS是每一个TCP报文段中的数据字段的最大长度。
三次握手
第一次握手:A向B发出连接请求报文段,SYN置1,选择一个初始序号seq=x。TCP规定。SYN报文段(即SYN=1)不能携带数据,但要消耗一个序号,此时TCP客户进程进入SYN-SENT(同步已发送状态)
第二次握手:B收到连接请求报文后,若同意,则发送确认报文段,SYN和ACK置1,选择一个初始序号seq=y,确认号ack=x+1。该报文段也不能携带数据,但要消耗一个序号。此时TCP服务器进程进入SYN-RCVD(同步收到)状态。
第三次握手:A收到B的确认后,要向B给出确认。确认报文段ACK置1,序号seq=x+1,确认号ack=y+1。TCP的标准规定,ACK报文段可以携带数据。这时TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态。B收到A的确认后,也进入ESTABLISHED状态。
四次挥手
第一次挥手:A的客户端进程向B发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
第二次挥手:B服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,A客户端向B服务器的方向就释放了,这时候处于半关闭状态,即A客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
A客户端收到B服务器的确认请求后,此时,A客户端就进入FIN-WAIT-2(终止等待2)状态,等待B服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
第三次挥手:B服务器将最后的数据发送完毕后,就向A客户端发送连接释放报文,FIN=1,ack=u+1(重复上次已发送过的确认号),由于在半关闭状态,B服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,B服务器就进入了LAST-ACK(最后确认)状态,等待A客户端的确认。
第四次挥手:A客户端收到B服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过时间等待计时器(TIME-WAIT timer)设置的时间2MSL(最长报文段寿命),当A客户端撤销相应的TCB后,才进入CLOSED状态。
B服务器只要收到了A客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
时间MSL:叫做最长报文段寿命(Maximum Segment Lifetime),RFC 793建议设为2分钟,对于现在的网络,MSL = 2分钟可能过长,因此可根据实际情况而定。
保活计时器(keepalive timer):若客户端已主动与服务器建立了TCP连接,之后客户端突发故障。为了使服务器不一直等待,使用了保活计时器。服务器每收到一次客户数据,就重置保活计时器,时间一般为2小时。若2小时未收到客户的数据,就发送一个探测报文段,以后则每隔75秒发送一次。若一连发送10个探测报文段后客户仍无响应,则认为客户端发生故障,关闭连接。