在网络协议中,TCP协议算是最常见的协议之一了。和UDP相比,TCP需要在传送数据前先建立起连接,结束后释放连接。TCP的特性也就意味着它不支持广播或多播服务。也因为TCP的需要稳定可靠、面向连接的运输服务,不可避免就要增加很多开销,比如确认、流量控制、计时器等连接管理等。
TCP的特点:
- 是面向连接的运输层协议。也就是说传送数据前建立连接,传送完成后需要释放链接。
- 每一条TCP连接只能有两个端点,每条TCP的连接只能是点对点的。
- TCP提供可靠交付服务。通过TCP传送的数据,无差错、不丢失、不重复,并且按序达到。
- TCP提供全双工服务。TCP允许双方在通信的过程中,任何时候都可以发送信息给对方。
- 面向字节流。TCP把从应用程序交付的数据看成一连串的无结构的字节流。
先来讲讲TCP报文的首部格式及每部分的含义。
源端口、目的端口:各占两个字节,分别是写入源端口号和目的端口号。TCP的分用功能也主要是通过端口实现的。
序号:因为TCP是面向字节流的,在TCP连接中的每一个字节都按顺序编号。
确认号:是期望收到对方下一个报文段的第一个数据字节的序号。假设收到方收到一个一个报文段,序号字段值为100,数据长度为500,那么表明收到方收到了600之前所有的数据,那么现在的确认号就是601。也就是说,确认号=N,则到N-1之前的所有数据都正确收到了。
数据偏移:指得是TCP报文段的首都长度。最大长度为60字节,即选项部分长度不可以超过40字节。
保留:目前置1,保留为以后使用。
紧接着的6个控制位说明了报文的性质。
URG:紧急位。置1时表示当前报文中有紧急数据,尽快传送。发送端把紧急的报文数据插入到当前报文段数据的最前面,在紧急数据后面的依然是普通数据。
ACK:置1时表示确认号有效,为0时,确认号无效。
PSH:当两个应用进程交互式通信时,一方进程希望在发送信息后就能收到对方的回应,这个时候就要使用PSH操作。把PSH置1,并立刻创建一个报文发送,接收方收到报文时尽快交付应用进程,不会等到整个缓存满了后在进行发送。
RST:RST置1时,会释放连接,然后重新建立连接进行数据发送。也可以拒绝非法报文段的接收。
SYN:建立连接时同步序列号。当SYN=1,ACK=0表示这是一个连接请求报文段,如果对方同意则 SYN=1, ACK=1。
FIN:用来释放一个连接。当FIN=1时,表示数据已经发送完毕,并要求释放连接。
窗口:窗口值是作为接收方让发送方设置每次发送数据多少的依据。
检验和:占2个字节,检验和字段检验的范围包括首部和数据这两个部分。
紧急指针:占2个字节,紧急指针只在URG=1时才有意义,它指出本报文段中有紧急数据的字节数。
值得注意的是URG和PSH之间的区别,URG是指明当前数据报中有紧急数据,但不会立刻发送,只是把紧急数据放在了要发送数据报的首部,等到整个缓冲区满了才发送。而PSH是不管写了多少,都会立刻把当前紧急数据发送出去。
可靠传输的工作保证
TCP发送的报文是要交给IP层传递的,但IP层只能提供最大努力传输,也就是说IP层的传输是不可靠的传输。那么,TCP必须采取一定的措施要保证两个运输层之间的通信变得可靠起来。
对我们来说,总是希望传输的数据文件等,可以完全无误地传输给目的地。也就是说:传输的信道是不可以发生错误的;不管发送方传输得多么快,接收方总是来得及处理收到的数据。
TCP可靠传输的工作保证有四个方面:
- 序号和确认序号的按序到达,以及可以确认的丢包序号
- 超时重传机制
- 流量控制
- 拥塞控制
- 定时器
定时器
在这里我们主要说说定时器。在TCP中的定时器主要有四种:超时重传定时器、坚持定时器、保活定时器、时间等待定时器。
1. 超时重传定时器
为了保证TCP的可靠传输,控制丢失的报文段或丢弃的报文段,也就是对报文段确认的等待时间。当TCP发送报文段时,就创建这个特定报文段的超时重传定时器。设置定时器后可能发生两种情况:若在定时器失效之前收到对报文段的确认,则撤销计时器;若在收到对特定报文段的确认之前定时器超时,则重传该报文,并把定时器复位。
2. 坚持定时器
坚持定时器是专门针对零窗口的处理方法。当发送端收到零窗口的确认时,就启动坚持定时器。当坚持定时器截止期到时,发送端TCP就发送一个特殊的报文段,叫探测报文段,这个报文段只有一个字节的数据。探测报文段也有序号,但序号永远不需要确认,甚至在计算对其他部分数据的确认时这个序号也被忽略。探测报文段提醒接收端TCP,确认已丢失,必须重传。
坚持定时器的截止期设置为重传时间的值,但若没有收到从接收端来的响应,则发送另一个探测报文段,并将坚持定时器的值加倍和并复位,发送端继续发送探测报文段,依此类推,直到这个值增大到阈值为止(通常为60秒)。之后,发送端每隔60s就发送一个报文段,直到窗口重新打开为止。
3.保活定时器
主要是用来确认和客户的连接行为。每当服务器收到客户的信息,就将保活定时器复位,而超时通常设置2小时。若服务器超过2小时还没有收到来自客户的信息,就发送探测报文段,若发送了10个探测报文段(每75秒发送一个)还没收到响应,则终止连接。
4.时间等待定时器
在连接终止期使用,当TCP关闭连接时,并不认为这个连接就真正关闭了。为了使其可靠,在时间等待期间,连接还处于一种中间过度状态,这样的状态就是时间等待定时器。TIME_WAIT状态就是用来重发可能丢失的ACK报文,一般这个计时器的值通常设置为报文生存周期的两倍。
讲完了这些终于可以进入正式话题说说,TCP的三次握手四次挥手究竟是怎么回事了。
TCP的运输连接管理
1.TCP的连接建立
为了保证其可靠性,在连接的时候要解决三个问题:
- 双方都可以确认对方的存在。如果有一个人没有确认到,都是不可靠的。
- 双方要协商在传输过程中使用的参数信息。
- 能够对运输的实体资源进行合理的分配。
假设现在由客户机主动发起了连接,服务器就需要先创建一个传输控制块TCB,准备接受客户进程的连接请求。此时客户端也是如此,建立传输控制块TCB。紧接着就向服务器发送了连接请求报文段,这时,SYN同步为1,同时会选择一个初始序列号x。当服务器接收到了后,如果同意建立连接,将回复一个报文段,SYN和ACK置1,回复自己的初始序列号,并且返回确认序列号x+1。当客户收到服务器的确认后,还要向服务器给出确认。然后才能开始数据传送。
在这里使用三次握手是非常必要的。假设TCP的连接是两次握手,当客户向服务器发起了请求,这个报文却因为网络原因滞留了,一直没有被服务器收到。当服务器收到后以为是客户发起的连接请求,于是对这个请求进行了回应,然后等待客户发来数据。然而这个时候客户并没有想连接的意思,所以不会向服务器发送数据。那么这个时候就会产生很多废弃的链接,浪费了很多资源。三次握手就可以有效预防这种情况发生。
2.TCP的连接释放
理解了建立连接,那么关闭连接也就比较好理解了。关闭连接的过程如下:
- 先由客户发起关闭请求
- 服务器应答客户的关闭请求
- 服务器向客户发送关闭连接请求
- 客户端应答关闭连接请求
当客户发起关闭连接请求的时候,服务器会立刻发出确认,这个时候服务器就进入了CLOSE_WAIT状态,与此同时通知高层应用进程。因为客户到服务器的连接已经释放了,TCP连接就处于一个半连接状态。也就是说客户不能向服务器发送数据,但服务器发送的数据仍要接收。当客户收到服务器的确认后,就进入了FIN _WAIT状态,等待服务器发来连接释放报文段。当客户收到这个报文时,必须要做出确认。至此,连接完成释放,双方都进入到CLOSED状态。
为什么发送确认后仍要等待2MSL呢?在这里有两个原因。一是:保证客户的确认报文能够被服务器接收。假设确认的报文因为网络延迟,服务器首先会向客户超时重传最后一次的报文,接着客户重传一次确认,重新启动计时。最后,客户和服务器都正常进入到CLOSED状态。二是:客户发送完最后一个报文段后,2MSL后本地连接的所有报文段都会从网络中消失。下一次建立连接时就不会出现旧的连接请求报文段。
除了时间等待定时器,这里还会使用保活定时器。假设因为主机故障,服务器不能接收来自客户发送的数据,如果没有定时器,那么服务器一直仍处于等待状态,这样非常浪费资源。所以需要一个保活定时器,每次发送数据后,就重新设置保活定时器,时间通常为2小时。如果两小时还没有收到数据,那么就发送一个探测报文段,以后每隔75分钟发送一次,若连续发送10个探测报文段仍没有响应,那么就关闭这个连接。