传输控制协议TCP概述

最近在看《计算机网络》这本书,感觉写的挺好的。现在学到了TCP部分,我对TCP部分做了笔记,记录了一些重点,也记录了一些自己的想法,在此分享。


TCP最主要的特点

  1. TCP是面向连接的运输层协议。
  2. 每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的。
  3. TCP提供可靠交付的服务。无差错、不丢失、不重复,并且按序到达。
  4. TCP提供全双工通信。TCP允许双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据放入缓存,上层的应用进程在合适的时候读取缓存中的数据。
  5. TCP是面向字节流的。这点不像UDP那样面向报文,对应用层交下来的报文 “既不合并,也不拆分” 。TCP只是把它们都看作是一连串无结构的字节流,并不了解所传送的字节流的含义,接收时的数据块跟发送时数据块数量可能不一样,但是总数据不会缺失。例如可能发送方应用程序交个发送方TCP有10个数据块,但接收方TCP可能只用了5个数据块就把收到的字节流交付上层的应用程序,虽然总共的字节流是一样的。将字节流还原成有意义的数据的任务,就交给应用层来完成。

套接字socket

TCP把连接作为最基本的抽象。

TCP连接的端点叫做套接字(socket)或插口。根据RFC 793的定义:端口号拼接到IP地址即构成了套接字。因此,套接字的表示方法是在点分十进制的IP地址后面写上端口号,中间用冒号或逗号隔开。即:

套接字socket = (IP地址 : 端口号)

每一条TCP连接唯一的被通信两端的两个端点(即两个套接字)所确定。即:

TCP连接 ::= {socket1, socket2} = {IP1:port1}, {IP2:port2}

需要注意的是,同一个名词socket可能有不同的意思,此处的socket仅仅是TCP连接的端点。

TCP报文段的首部格式

在这里插入图片描述
如图为TCP报文段的首部格式。

首部固定部分各字段意义如下:

1)源端口目的端口
各占2个字节,分别写入源端口和目的端口。

2) 序号
占4字节。序号范围是[0,232 - 1],共232(即4294967296)个序号。序号增加到232-1后,下一个序号就又回到0。也就是说,序号使用mod 232运算。TCP是面向字节流的。在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则是指的是本报文段所发送的数据的第一个字节的序号。例如,一报文段的序号是301,而接待的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是301,最后一个字节的序号是400。显然,下一个报文段(如果还有的话)的数据序号应当从401开始,即下一个报文段的序号字段值应为401。这个字段的序号也叫“报文段序号”。

3) 确认号
占4字节,是期望收到对方下一个报文段的第一个数据字节的序号。例如,B正确收到了A发送过来的一个报文段,其序号字段值是501,而数据长度是200字节(序号501~700),这表明B正确收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701。注意,现在确认号不是501,也不是700,而是701。

总之:若确认号 = N,则表明:到序号N-1为止的所有数据都已正确收到。

4) 数据偏移
占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。这个字段实际上是指出TCP报文段的首部长度。由于首部中还有长度不确定的选项字段,因此数据偏移字段是必要的,但应注意,“数据偏移”的单位是32位字(即以4字节的字为计算单位)。由于4位二进制数能表示的最大十进制数字是15,因此数据偏移的最大值是60字节,这也是TCP首部的最大字节(即选项长度不能超过40字节)。

5) 保留
占6位,保留为今后使用,但目前应置为0 。
下面有6个控制位,用来说明本报文段的性质。

6) 紧急URG(URGent)
当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快发送(相当于高优先级的数据),而不要按原来的排队顺序来传送。例如,已经发送了很长的一个程序要在远地的主机上运行。但后来发现了一些问题,需要取消该程序的运行,因此用户从键盘发出中断命令。如果不使用紧急数据,那么这两个字符将存储在接收TCP的缓存末尾。只有在所有的数据被处理完毕后这两个字符才被交付接收方的应用进程。这样做就浪费了很多时间。

当URG置为1时,发送应用进程就告诉发送方的TCP有紧急数据要传送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍然是普通数据。这时要与首部中紧急指针(Urgent Pointer)字段配合使用。

7) 确认ACK(ACKnowledgment)
仅当ACK = 1时确认号字段才有效,当ACK = 0时确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1。

8) 推送 PSH(PuSH)
当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。

9) 复位RST(ReSeT)
当RST=1时,表名TCP连接中出现了严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立传输连接。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。

10) 同步SYN(SYNchronization)
在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。

11) 终止FIN(FINis,意思是“完”、“终”)
用来释放一个连接。当FIN=1时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接。

12) 窗口
占2字节。窗口值是[0,216-1]之间的整数。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值被视为接收方让发送方设置其发送窗口的依据

例如,发送了一个报文段,其确认号是701,窗口字段是1000.这就是告诉对方:“从701算起,我(即发送方报文段的一方)的接收缓存空间还可接受1000个字节数据(字节序号是701~1700),你在给我发数据时,必须考虑到这一点。”

总之:窗口字段明确指出了现在允许对方发送的数据量。窗口值经常在动态变化。

13) 检验和
占2字节。检验和字段检验的范围包括首部和数据这两部分。和UDP用户数据报一样,在计算检验和时,要在TCP报文段的前面加上12字节的伪首部。伪首部的格式和UDP用户数据报的伪首部一样。但应把伪首部第4个字段中的17改为6(TCP的协议号是6);把第5字段中的UDP中的长度改为TCP长度。接收方收到此报文段后,仍要加上这个伪首部来计算检验和。若使用IPv6,则相应的伪首部也要改变。

14) 紧急指针
占2字节。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据) 。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为0时也可以发送紧急数据。

15) 选项
长度可变,最长可达40字节。当没有使用“选项”时,TCP的首部长度是20字节。

TCP最初只规定了一种选项,即最大报文段长度MSS(Maximum Segment Size)。注意MSS这个名词含义。MSS是每一个 TCP报文段中的数据字段的最大长度。数据字段加上TCP首部才等于整个的TCP报文段。所以MSS并不是整个TCP报文段的最大长度,而是“TCP报文段长度减去TCP首部长度”。

为什么要规定一个最大报文长度MSS呢?这并不是考虑接受方的接收缓存可能存放不下TCP报文段中的数据。实际上,MSS与接收窗口值没有关系。我们知道,TCP报文段的数据部分,至少要加上40字节的首部(TCP首部20字节和IP首部20字节,这里还没有考虑首部中的可选部分)才能组装成一个IP数据报。若选择较小的MSS长度,网络的利用率就降低。设想在极端情况下,当TCP报文段只含有1字节的数据时,在IP层传输的数据报的开销至少有40字节(包括TCP报文段的首部和IP数据报的首部)。这样,对网络的利用率就不会超过1/41。到了数据链路层还要加上一些开销。但反过来,若TCP报文段非常长,那么在IP层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片组成成原来的TCP报文段,当传输出错时还要进行重传,这些也都会使开销增大。

因此,MSS应尽可能大些,只要在IP层传输时不需要分片就行。由于IP数据报所经历的路径是动态变化的,因此在这条路径上确定的不需要的分片的MSS,如果改走另一条路径就可能需要进行分片。因此最佳的MSS是很难确定的。在连接过程中,双方都把自己能够支持的MSS写入这一字段,以后就按照这个数值传输数据,两个传送方向可以有不同的MSS值。若主机未填写这一项,则MSS的默认值是536字节长。因此,所有在互联网上的主机都应该接受的报文段长度是536+20(固定首部长度)=556字节。

后来又增加了几个选项如窗口扩大选项时间戳选项选择确认选项等。

窗口扩大选项是为了扩大窗口。我们知道,TCP首部中窗口字段长度是16位,因此最大的窗口大小为64K字节。虽然这对早期的网络是足够用的,但对于包含卫星信道的网络,传播时延和宽带都很大,要获得高吞吐量需要更大的窗口大小。

窗口扩大选项占3字节,其中有一个字节表示移位值S。新的窗口值等于TCP首部中的窗口位数从16增大到(16+S)。移位值允许使用的最大值是14,相当于窗口最大值增大到2(16+14)-1=230-1。
窗口扩大选项可以在双方初始建立TCP连接时进行协商。如果连接的某一端实现了窗口扩大,当它不再需要扩大其窗口时,可发送S=0选项,使窗口大小回到16。

时间戳选项占10字节,其中最主要的字段是时间戳字段(4字节)和时间戳回送回答字段(4字节)。时间戳选项有以下两个概念:

第一、 用来计算往返时间RTT。发送方在发送报文段时把当前时钟的时间值放入时间戳字段,接收方在确认该报文段时把时间戳字段复制到时间戳回送回答字段。因此,发送方在收到确认报文后,可以准确地计算出RTT来。

第二、 用于处理TCP序号超过 232 的情况,这又称为防止序号绕回PAWS。我们知道,TCP报文段的序号只有32位,而每增加 232个序号就会重复使用原来用过的序号。当使用高速网络时,在一次TCP连接的数据传送中序号很可能被重复使用。例如,当使用1.5Mbit/s的速度发送报文段时,序号重复要6小时以上。但若用2.5Gbit/s的速率发送报文段,则不到14秒钟序号就会重复。为了使接收方能够把新的报文段和迟到很久的报文段区分开,则可以在报文段中加上这种时间戳。

可靠传输的工作原理

TCP发送的报文段是交给IP层传送的,但IP层只能提供尽最大努力交付,也就是说,TCP下面的网络提供的是不可靠的传输。因此TCP必须采取适当的措施才能使得两个运输层之间的通信变得可靠。为此就有下面的协议。

停止等待协议

  • 无差错情况

    A发送分组给B,发完暂停发送,等待B的确认。B收到了就向A发送确认,A在收到了B的确认后,就再发送下一个分组。

  • 出现差错情况

    B在接收时出了差错,就丢弃掉出错部分,或许分组在传输中丢失掉了,这时B不会发送任何信息。可靠传输协议设计为:A只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢了,因而重传前面发送过的分组。这叫做超时重传。要实现超时重传,就要在每发送完一个分组时设置一个超时计时器。如果在超时计时器到期之前收到了对方的确认,就撤销已设置的超时计时器。

    应注意:

    • A在发送完一个分组后,必须暂时保留已发送分组的副本(在发生超时重传时使用)。只有在收到相应的确认后才能清楚暂时保留的分组副本;
    • 分组和确认分组必须编号。如此才能明确是哪一个发送出去的分组收到了确认,而哪一个分组还没有收到确认;
    • 超时计数器设置的重传时间应当比数据在分组传输的平均往返时间更长一些。
  • 确认丢失和确认迟到

    • 确认丢失
      A在设定的超时重传时间内没有收到确认,并无法知道是自己发送的分组出错、丢失,或者是B发送的确认丢失了。因此A在超时计时器到期后就要重传分组。此时B又收到了A发来的重复分组,这时应采取两个行动:
      第一,丢弃这个重复的分组,不用向上交付;
      第二,向A发送确认。

    • 确认迟到
      B对分组的确认迟到了,A会收到重复的确认,对重复的确认的处理是:收下后就丢弃。B同样要丢弃重复的分组,并重传确认分组。

A最终总会收到对所有发出的分组的确认。如果A不断重传分组但总是收不到确认,就说明通信线路太差,不能进行通信。

向上述的确认和重传机制,我们就可以在不可靠的传输网络上实现可靠的通信。像上述的这种可靠传输协议常称为自动重传请求ARQ(Automatic Repeat Request)。意思是重传的请求是自动进行的。接受方不需要请求发送方重传某个出错的分组。

为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停顿下来等待对方的确认。这样可使信道上一直有数据不间断的传送。

当使用流水线传输,就要使用下面的连续ARQ协议滑动窗口协议

连续ARQ协议

发送方维持一个发送窗口,一次把窗口内所有分组全部发送出去,不必收到确认后再发送下一个。每收到一个确认,发送窗口就往前移动一个分组。

接受方一般是累计确认的方式。在收到几个分组后,对按序到达的最后一个分组发送确认,表示到这个分组为止的所有分组都已经正确收到了。

累计确认的优点是:容易实现,即使确认丢失也不用重传;

缺点是:不能向发送方反映出接收方已经正确接收到的所有分组的信息。例如,如果一共发送了5个分组,由于某些原因第3个分组丢失了,这时接收方只能发送到第2个分组的确认信息,发送方无法得知后三个分组是否送达,于是只好把这三个重传一次,称为 Go-back-N(回退N)。可见当通信质量不好的时候,会出现重传很多次的情况,带来不好的影响。

滑动窗口协议

TCP的滑动窗口协议以字节为单位。若假定A收到了B发来的确认报文段,其中窗口是20字节,确认号是31(这表明B期望收到的下一个序号为31,而序号30为止的数据已经收到了)。根据这两个数据,A构造出自己的发送窗口,即从31到50的窗口。

发送窗口是什么?它表示,在未收到B的确认的情况下,A可以连续把窗口内的数据发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。

假设缓存窗口序号是从左到右依次增大的,那么左边设为窗口后沿,右边为窗口前沿,那么窗口后沿后边的部分表示已发送且已确认,前沿前边的部分表示不允许发送,因为接受方窗口不包括这里,没有生成对此的缓存。

发送窗口的位置由窗口前沿和后沿共同确认。其后沿变化有两种情况,即不动(没有收到新的确认)和前移(收到了新的确认)。后沿不可能向后端移动,因为后沿之后的数据均已经被确认过了。前沿一般是不断向前移动的,但也有可能不动,比如:一种是没有收到新的确认,对方通知的窗口大小不变;第二种是收到了新的确认,同时对方通知的窗口也缩小了,恰好保持前沿不动。前沿也有可能向后收缩(对方通知的窗口缩小了),但是TCP标准不推荐这么做,前面发送过,再进行取消,会产生一些错误。

发送窗口始终进行“发送-接收确认-调整窗口-发送“的过程。但是存在一些特殊情况,如:A发送窗口所有数据都发送给了B,B也返回了确认,但是所有确认都滞留在网络中。A没有收到确认一段时间后,就只好重传这些数据,即超时重传,直到收到B的确认为止。

TCP的流量控制与拥塞控制

可以参考我的另一篇文章:TCP流量控制与拥塞控制原理分析

TCP的连接与连接释放

可以参考我的另一篇文章:TCP知识点总结