TCP协议简介

TCP协议(Transmission Control Protocol)是一种传输层的通信协议。(传输层位于五层模型的第四层)

TCP协议是为保证数据传输的可靠性而设计的。

下面我们会从三方面来讲述TCP相关的知识:

  • TCP报文的结构
  • 连接的建立
  • 数据的传输 - 滑动窗口

TCP的报文结构

TCP报文结构

TCP消息的报文结构

TCP的报文段分为首部数据两部分。TCP的全部功能都体现在它的首部中各字段。

通常情况下,报文段首部的长度是20字节。

源端口、目的端口

各占2个字节。标识源端口号和目的端口号。

序号

占4个字节。标识当前报文段所发送的数据的第一个字节的序号。比如当前报文段的序号字段值是101,携带的数据有200字节,这表明:当前报文段的数据的第一个字节序号是101,最后一个字节的序号是300。所以,下一个报文段的数据序号(或序号字段值)应该为301。

确认号

占4个字节。标识期望收到对方下一个报文段的第一个数据字节的序号。确认号值 = 上一次接收的报文段的序号值 + 上一次接收的报文段数据部分的字节数。

数据偏移

占4个字节。标识TCP报文段的起始位置与TCP报文段数据部分的起始位置间隔的字节数。即表示TCP报文段首部的长度。

保留

占6个字节。为TCP协议未来扩展预留的字节

URG、ACK、PSH、RST、SYN、FIN

各占1个字节。这里的六个首部字段用于说明当前报文段的性质。

首部字段 描述
URG 紧急(URGent)。当URG=1时,表示当前报文有紧急数据,应优先传送,不应按原来的队列顺序传送。
ACK 确认(ACKnowlegment)。ACK=1表示确认号有效,ACK=0表示确认号无效。TCP规定,在成功建立连接后,所有传送的报文段都必须设定ACK=1。
PSH 推送(PUSH)。PSH=1时,表示当前的信息交互有时效性需求。接收方收到标识了PSH=1的报文段后,应尽快将信息推给上一层的应用程序,而不是等待缓存填满再提交。主要在对时效性要求较高的即时通讯中使用。
RST 复位(ReSet)。RST=1时,表示当前TCP连接中出现了错误,必须释放连接,并重新建立连接。
SYN 同步(SYNchronization)。在建立连接期间,用于同步序号。SYN=1表示当前报文处于连接建立阶段。
FIN 终止(Finis)FIN=1表示释放连接。当报文段出现FIN=1时,表明该报文段处于释放连接阶段。

窗口

占2个字节。窗口值表示从当前报文的确认号开始算起,允许对方发送的数据量。由于缓存空间的限制,避免对方发送数据量过大加大缓存压力,需要告诉对方当前缓存空间的大小,作为对方调整发送速率的依据。

检验和

占2个字节。用于检验报文段的正确性。

紧急指针

占2个字节。仅在URG=1时有效。表示当前报文段紧急数据的字节数。紧急指定指出了紧急数据末尾在当前报文段中的位置。

选项

长度可变,最大允许长度为40字节。目前几种常见的选项有:MSS(Maximum Segment Size)、窗口扩大选项、时间戳选项、选择确认。

连接的建立(三次握手)

TCP-三次握手

TCP - 三次握手

第一次握手

A向B发送一个连接请求报文段。该报文段仅含有报文首部,不携带报文数据。报文段的首部SYN=1,表示当前处于TCP连接建立阶段。同时随机生成一个序号seq=x。此时A处于SYN-SENT状态。

第二次握手

B收到A的报文,并向A发送确认报文。该报文段仅含有报文首部,不携带报文数据。在确认报文中,设置SYN=1, ACK=1, 确认号ack=x+1。除此之外,为当前报文生成一个初始序号seq=y。此时B处于SYN-RCVD状态。

第三次握手

A收到B的确认后,再次向B发出确认报文。设置ACK=1(后续所有的报文ACK都等于1),确认号ack=y+1,序号seq=x+1。A进入ESTABLISHED状态。

连接的释放(四次挥手)

连接的释放过程与连接建立类似,这里不再详述。

数据的传输 - 滑动窗口

在描述数据的传输原理前,为了便于描述,我们假定数据的传输只在一个方向上进行。即A作为发送方发送数据,B作为接收方接收并确认数据。(事实上,由于数据包的来回传输方向切换,A和B随时都在进行**接收方/ **的角色切换)

下面的例子中,为了便于说明,我们把字节编号都取得很小。

例1 - 发送窗口介绍

假设A收到B发送的确认报文段,报文段的窗口值是20(字节),确认号是31(即B已收到序号31之前的数据,期望收到的下一个序号是31)。根据这两个数据,A就构建了自己的发送窗口:

发送窗口图示

TCP - 发送窗口图示

上图中,左右两条虚线包围的区域即是发送窗口。只有发送窗口中的数据包才允许被连续发送。显然,发送窗口越大,就表示收到确认前可以发送更多的数据出去。即发送窗口越大,传输效率越高。

发送窗口的后沿变化可能存在两种情形:

  1. 在未收到确认报文期间保持不动。
  2. 在收到确认报文后将向前移动

发送窗口的前沿变化可能有三种情形:

  1. 在未收到确认报文期间,或收到确认报文,但通知窗口值缩小,且缩小幅度刚好与已发送报文字节数相等,则保持不动。
  2. 收到确认报文,但通知窗口值缩小,且缩小幅度大于已发送报文字节数,则向后收缩(这种情况容易引发错误,TCP标准不建议这么做)。
  3. 在收到确认报文,通知窗口值不变或变大,则前移。

例2 - 窗口的移动

发送窗口的移动

现在A向B发送了31-41的报文,这些已发送但未得到确认的报文并不会再缓存中删除,因为可能会存在B端未收到而需要再次发送的情况。

B的接收窗口随后收到了序号32、33的报文,这些报文并没有按序到达(序号31报文未收到)。根据接收方只能对按序收到的报文的最高序号给出确认的原则,B发送的确认报文的确认号仍是31

现假设B收到了序号31的报文,此时接收方按序收到的报文的最高序号变为33。接收方会做以下几件事:

  1. 接收窗口的后沿向前移动至序号34的报文。
  2. 向A发送确认号为34的报文
  3. 将序号31-33报文交付至应用层,并在当前接收缓存中删除这些数据。
发送窗口的移动2

A收到确认号34的报文,接收窗口的后沿向前移动至序号34报文。假设接收的报文窗口值仍为20,那么接收窗口的前沿将向前移动至序号53报文前面。

此时B陆续收到了序号37、38、40的报文,由于这些报文没有按序到达,只能暂存于接收窗口中。

发送窗口的移动2

A继续发送完序号42-53的数据后,发送窗口中所有的报文都处于已发送,(部分)未确认状态。此时可用窗口变为零,必须停止发送。此时可能存在的几种情况:

  1. B已发出确认报文,但是滞留在网络中。
  2. B已发出确认报文,但已丢失。
  3. B未发出确认报文。

为了保证传输的可靠性。A应认为B没有收到这些数据。A应该在一段时间(超时计时器控制)后重发报文,直到得到B的确认报文为止。