TCP是TCP/IP协议中面向连接的传输层协议,它提供全双工和可靠交付的服务。TCP与UDP最大的区别在于,TCP是面向连接的,而UDP是无连接的。TCP比UDP要复杂得多,除了具有面向连接和可靠传输的特性外,TCP还在运输层使用了流量控制和拥塞控制机制。
一、TCP的固定首部
关于它首部中的各个字段的用途:
(1)源端口和目的端口:各占两个字节,定义了在主机中发送和接收该报文段的应用程序的端口号,用于传输层的复用和分用。
(2)序号:占四字节,是为了确保传输数据的按序到达,序号的值代表本报文段所发送的数据的第一个字节的序号。
(3)确认序号:占四字节,是期望收到对方的下一个报文段的第一个数据字节的序号。(TCP采用的是累积确认)
(4)首部长度:占四位,TCP报文段首部的长度(TCP首部的最小长度为固定的20字节)
(5)保留:占六位,待以后扩展使用。
(6)六个标志位,下面再讨论。
(7)窗口:占两个字节,描述接受缓冲区的大小,控制对方发送的数据量。
(8)检验和:占两个字节,检验范围包括首部和数据两部分。
(9)紧急指针:占两个字节,与标志位中URG配合使用。
这六个标志位,说明此报文段的性质:
紧急URG(urgent):当URG=1时,表明紧急指针字段有效。他告诉系统此报文段中有紧急数据,应尽快推送(相当于高优先级的数据),而不需要按原来的排队顺序来传送。例如:已经发送了很长的一个程序要在远地的主机上运行。但后来发现了一些问题,需要取消该程序的运行。因此用户从键盘发出终端命令(Ctrl+C)。如果不使用紧急数据,那么这两个字符将存储在接受TCP的缓存末尾。只有在所有的数据被处理完毕后这两个字符才被交付接收方的应用进程。这样做就浪费了很多时间。因此当URG置1时,发送应用进程就告诉发送方的TCP有紧急数据要传送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍是普通数据。这时要与紧急指针字段配合使用。
确认ACK:只有当ACK字段为1时,确认序号字段才有效。
推送PSH(push):当两个应用进程进行交互式的通信时,有时在一端的应用进程希望再键入一个命令后立即就能够收到对方的响应。在这种情况下,TCP就可以使用推送操作。这时,发送方TCP把PSH置1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快交付接收应用进程,而不再等到整个缓存都填满了再向上交付。
复位RST(reset):当RST=1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立连接。
同步SYN:用来建立一个连接,当SYN置1时,表示这是一个连接请求或连接接受保文。
终止FIN(finish):用来释放一个连接,当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放传输连接。
值得注意的是,紧急URG字段与推送PSH字段都有着推送紧急数据的含义,那么它们的区别在哪里呢:
URG是紧急标志位,当它为1的时候表明此报文段中含有紧急数据,并且紧急数据排在普通数据的前面,当接受端收到此报文后后必须先处理紧急数据。而后再处理普通数据。
PSH是催促标志位,当发送端将PSH置为1时,TCP会立即创建一个报文并发送。接受端收到PSH为1的报文后就立即将接受缓冲区内数据向上交付给应用程序,而不是等待缓冲区满后再交付。两者都可理解为处理紧急数据的标志位,只是处理方法不同。URG的紧急数据仅在报文内,而PSH的紧急数据还在接收缓冲区内。
二、TCP的可靠数据传输
(1)请求与应答
(2)保证数据的按序到达
(3)丢包重传
(4)面向连接
(5)通过滑动窗口来进行流量控制
(6)超时重传
(7)网络拥塞控制
在TCP有七个定时器来确保它可以做到可靠传输: 1.“连接建立(connection establishment)”定时器在发送SYN报文段建立一条新连接时启动。如果在75秒内没有收到响应,连接建立将中止。
2.“重传(retransmission)”定时器在TCP发送某个数据段时设定。如果该定时器超时而对端的确认还未到达,TCP将重传该数据段。重传定时器的值 (即TCP等待对端确认的时间)是动态计算的,与RTT(报文段的往返时延)的估计值密切相关,且还取决于该报文段已被重传的次数。
3.“延迟ACK(delayed ACK)”定时器在TCP收到必须被确认但无需马上发出确认的数据时设定。如果在200ms内,有数据要在该连接上发送,延迟的ACK响应就可随着数据一起发送回对端,称为捎带确认。如果200ms后,该确认未能被捎带出去,则定时器超时,此时需要发送一个立即确认。
4.“持续 (persist)”定时器在连接对端通告接收窗口为0,阻止TCP继续发送数据时设定。由于连接对端发送的窗口通告不可靠(只有数据才会被确认,ACK不会被确认),允许TCP继续发送数据的后续窗口更新有可能丢失。因此,如果TCP有数据要发送,但对端通告接收窗口为0,则持续定时器启动,超时后向对端发送 1字节的数据,判定对端接收窗口是否已打开。
5.“保活(keep alive)”定时器在TCP控制块的so_options 字段设置了SOF_KEEPALIVE选项时生效。如果连接的连续空闲时间超过2小时,则保活定时器超时,此时应向对端发送连接探测报文段,强迫对端响应。如果收到了期待的响应, TCP可确定对端主机工作正常,在该连接再次空闲超过2小时之前,TCP不会再进行保活测试。如果收到的是RST复位响应, TCP可确定对端主机已重启。如果连续若干次保活测试都未收到响应, TCP就假定对端主机已崩溃,但它无法区分是主机故障还是连接故障。
6.“FIN_WAIT_2”定时器,当某个连接从FIN_WAIT_1状态变迁到FIN_WAIT_2状态并且不能再接收任何新数据时,FIN_WAIT_2定时器启动,设为10分钟。定时器超时后,重新设为75秒,第二次超时后连接被关闭。加入这个定时器的目的是为了避免如果对端一直不发送 FIN,某个连接会永远滞留在FIN _ WAIT_ 2状态(假设TCP不选用半打开功能)。
7.“TIME_WAIT”定时器,一般也称为2MSL定时器。2MSL指两倍的MSL,即最大报文段生存时间。当连接转移到TIME_WAIT状态,即连接主动关闭时,定时器启动。连接进入TIME_WAIT状态时,定时器设定为1分钟,超时后,TCP控制块被删除,端口号可重新使用。
三、TCP的连接管理
1.连接建立(三次握手)
主机A的TCP向主机B的TCP发送连接请求报文段(其首部中的同步位SYN置1),同时选择一个序号seq=x,这表明下一个报文段的第一个数据字节的序号是x+1;
主机B收到主机A的连接请求报文段后,如同意,则发回连接请求确认。在确认报文段中将SYN与ACK位都置1,确认号ack=x+1,同时也为自己选择一个序号seq=y;
主机A的TCP收到B连接请求的确认后,还要向B给出确认(否则如果主机B的连接请求确认报文丢失,而主机A又不向主机B作出任何回复,此时,主机B认为连接已经建立,而主机A并没有收到丢失的那条确认信息,则主机A隔一段时间又会向主机B发送连接请求,如此循环,主机B浪费了建立连接的资源,因为建立连接需要有对应的数据结构来描述该连接,这就是TCP建立连接为什么需要三次握手),其ACK置1,确认号ack=y+1,而自己的序号seq=x+1。
2.连接释放(四次挥手)
主机A的应用进程先向其TCP发出连接释放请求,并且不再发送数据。TCP通知主机B要释放从A到B这个方向的连接,把发往主机B的报文段首部的FIN置1,其序号seq=u,主机B的TCP收到释放连接通知后即发出确认,确认号是ack=u+1,主机B的TCP这时候通知高层应用进程,这样,从A到B的连接就释放了,连接处于半关闭状态。因为TCP是全双工的通信方式,这个时候相当于主机A已没有数据要发送,但如果主机B仍有数据要发送,主机A可以接收。
若主机B不再向主机A发送数据,则其应用进程就通知TCP释放连接,置FIN为1,使其序号为v(因为确认报文段如果不携带数据则不消耗序号),重复上次已发送过的确认序号ack=u+1,主机A需对此进行确认,把ACK置1,确认号ack=v+1,而自己的序号seq=u+1(因为根据TCP标准,FIN报文段要消耗一个序号),这样就把从B到A的反向连接释放掉。
主机A主动关闭连接时,会在发送最后一个ack后,进入TIME_WAIT状态,再停留2个MSL时间(MSL就max segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间IP数据包将在网络中消失 。MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒),进入CLOSED状态。(TIME_WAIT状态维持1~4分钟,在Windows操作系统下为四分钟)
为什么会存在TIME_WAIT状态:
1)可靠地实现TCP全双工连接的终止
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状态 。
2)允许老的重复分节在网络中消逝
TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个迟到的迷途分节到达时可能会引起问题。在关闭“前一个连接”之后,马上又重新建立起一个相同的IP和端口之间的“新连接”,“前一个连接”的迷途重复分组在“前一个连接”终止后到达,而被“新连接”收到了。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。