TCP(传输控制协议)详解

TCP 特点

  • TCP 是面向连接的运输层协议,在无连接的、不可靠的 IP 网络服务基础之上提供可靠交付的服务。为此,在 IP 的数据报服务基础之上,增加了保证可靠性的一系列措施。
  • TCP 是面向连接的运输层协议:每一条 TCP 连接只能有两个端点 (endpoint),每一条 TCP 连接只能是点对点的(一对一)。
  • TCP 提供可靠交付的服务。
  • TCP 提供全双工通信。
  • 面向字节流  :TCP 中的“流”(stream) 指的是流入或流出进程的字节序列。 面向字节流:虽然应用程序和 TCP 的交互是一次一个数据块,但 TCP 把应用程序交下来的数据看成仅仅是一连串无结构的字节流。

面向流的含义:

TCP的连接

Socket 有多种不同的意思:

  1. 应用编程接口  API  称为 socket API, 简称为 socket。
  2. socket API 中使用的一个函数名也叫作 socket。
  3. 调用 socket 函数的端点称为 socket。
  4. 调用 socket 函数时其返回值称为 socket 描述符,可简称为 socket。

如何实现可靠传输

  • 每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
  • 全双工通信的双方既是发送方也是接收方。
  • 假设仅考虑 A 发送数据,而 B 接收数据并发送确认。因此 A 叫做发送方,而 B 叫做接收方。

问题:A 如何知道 B 是否正确收到了 M1 呢?

解决方法:①超时重传   A 为每一个已发送的分组设置一个超时计时器。 A 只要在超时计时器到期之前收到了相应的确认,就撤销该超时计时器,继续发送下一个分组 M2 。

若 A 在超时计时器规定时间内没有收到 B 的确认,就认为分组错误或丢失,就重发该分组。

②确认丢失:若 B 所发送的对 M1 的确认丢失了,那么 A 在设定的超时重传时间内将不会收到确认,因此 A 在超时计时器到期后重传 M1。

假定 B 正确收到了 A 重传的分组 M1。这时 B 应采取两个行动:

        (1) 丢弃这个重复的分组 M1,不向上层交付。  (2) 向 A 发送确认。

③确认迟到:

  • B 对分组 M1 的确认迟到了,因此 A 在超时计时器到期后重传 M1。
  • B 会收到重复的 M1,丢弃重复的 M1,并重传确认分组。
  • A 会收到重复的确认。对重复的确认的处理:丢弃。

提高传输效率

1.流水线传输

2.连续 ARQ 协议(滑动窗口协议)

  • 发送窗口:发送方维持一个发送窗口,位于发送窗口内的分组都可被连续发送出去,而不需要等待对方的确认。
  • 发送窗口滑动:发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。
  • 累积确认:接收方对按序到达的最后一个分组发送确认,表示:到这个分组为止的所有分组都已正确收到了。

TCP首部格式

首部各字段的意义如下:

  • 源端口和目的端口:各占 2 个字节,分别写入源端口号和目的端口号。
  • 序号:占 4 字节。本报文段所发送的数据的第一个字节的序号。序号范围是 [ 0, 2^32 -1 ],共 2^32 个序号,序号增加到 2^32 -1 后,下一个序号就又回到 0。TCP是面向字节流的。在一个 TCP 连接中传送的字节流中的每一个字节都按顺序编号。整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则指的是本报文段所发送的数据的第一个字节的序号。
  • 确认号:占 4 字节,是期望收到对方下一个报文段的第一个数据字节的序号。只有在 ACK = 1 时确认号字段才有意义。由于序号字段有 32 位长,可对 4GB(即 4 千兆字节)的数据进行编号。在一般情况下,可保证当序号重复时,旧序号的数据早已通过网络到达终点了。
  • 数据偏移:占 4 位,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。这个字段实际上是指出 TCP 报文段的首部字段长度。由于首部中还有长度不确定的选项字段,因此数据偏移字段是必要的。但应注意,“数据偏移” 的单位是 32 位字。由于 4 位 二进制数能够表示的最大十进制数字是 15,因此数据偏移的最大值是 60 字节(15 * 32bit),这也是 TCP 首部的最大长度(即选项长度不能超过 40 字节 / 60 最大首部 -20 固定首部 = 40 选项长度)
  • 保留:占 6 位,保留位今后使用,但目前应置为 0 。
  • 6 个控制位:包括 URG、ACK、PSH、RST、SYN、FIN,用来说明本报文段的性质。
  • 窗口:占 2 字节。窗口指的是发送本报文段的一方的接收窗口。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。窗口字段明确指出了现在允许对方发送的数据量。窗口值经常在动态变化着。窗口值是 [ 0, 2^16 -1 ] 之间的整数。
  • 检验和:占 2 字节。检验和字段检验的范围包括首部和数据这两部分。和 UDP 数据报一样,在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部。伪首部的格式与 UDP 用户数据报的伪首部一样。但应把伪首部第 4 个字段中的 17 改为 6(TCP 的协议号是 6),把第 5 个字段中的 UDP 长度改为 TCP 长度。接收方收到此报文段后,仍要加上这个伪首部来计算校验和。若使用 IPv6,则相应的伪首部也要改变。
  • 紧急指针:占 2 字节。紧急指针仅在 URG = 1 时才有意义,它指出本报文字段中的紧急数据的字节数(紧急数据之后就是普通数据)。因此,紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP 就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为零时也可发送紧急数据。
  • 选项:长度可变,最长可达 40 字节。当没有使用 “选项” 时,TCP 的首部长度是 20字节。

TCP 有 6 个控制位,用来说明报文段的性质。

  1. 同步 SYN
  2. 确认 ACK
  3. 推送 PSH
  4. 紧急 URG
  5. 终止 FIN
  6. 复位 RST

同步 SYN

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

确认 ACK

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

推送 PSH

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

紧急 URG

        当 URG = 1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不要按原来的排队顺序来传送。

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

        紧急指针指出本报文字段中的紧急数据的字节数(紧急数据之后就是普通数据)。因此,紧急指针指出了紧急数据的末尾在报文段中的位置。

终止 FIN

用来释放一个连接。当 FIN = 1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。

复位 RST

        当 RST = 1时,表明 TCP 连接中出现严重差错(如由于主机奔溃或其他原因),必须释放连接,然后再重新建立运输连接。RST 置 1 还用来拒绝一个非法的报文段或拒绝打开一个连接。RST 也可称为 重建位 或 重置位。

选项

  • 最大报文段长度 MSS
  • 窗口扩大选项
  • 时间戳选项
  • 选择确认(SACK)


最大报文段长度 MSS

为什么要规定一个最大报文段长度 MSS 呢?实际上,MSS与接收窗口值没有关系。若选择较小的 MSS 长度,网络的利用率就降低。但反过来,若 TCP 报文段非常长,那么在 IP 层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片装配成原来的 TCP 报文段。当传输出错时还要进行重传。这些也都会时开销增大。

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

窗口扩大选项

窗口扩大选项是为了扩大窗口。窗口扩大选项占 3 字节,其中有一个字节表示 移位值 S。新是窗口值等于 TCP 首部中的窗口位数从 16 增大到 (16 + S)。移位值允许使用的最大值是 14。

窗口扩大选项可以在双方初始建立 TCP 连接时进行协商。如果连接的某一端实现了窗口扩大,当它不再需要扩大其端口时,可发送 S= 0 的选项,使窗口大小回到 16。

时间戳选项

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

  • 用来计算往返时间 RTT。发送方在发送报文段时把当前时钟的时间值放入时间戳字段,接收方在确认该报文时把时间戳字段值复制到时间戳回送回答字段。因此,发送方在收到确认报文后,可以准确地计算出 RTT 来。
  • 用于处理 TCP 序号超过 2^32 的情况,这又称为防止序号绕回 PAWS。TCP 报文段的序号只有 32 位,而每增加 2^32 个序号就会重复使用原来用过的序号。为了使接收方能够把新的报文段和迟到很久的报文段区分开,可以在报文段中加上这种时间戳。

选择确认(SACK)

若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送缺少的数据而不重传已经正确到达接收方的数据?答案是可以的。选择确认就是一种可行的处理方法。

然而,SACK 文档并没有指明发送方应当怎样响应 SACK。因此大多数的实现还是重传所有未被确认的数据块。

 [ 关于首部部分内容的原文引自链接:                        https://blog.csdn.net/weixin_41154070/article/details/123707953  ]

TCP连接的建立与释放

“三报文握手”建立TCP连接

TCP 建立连接的过程叫做握手。 采用“三报文握手”:在客户和服务器之间交换三个 TCP 报文段,以防止已失效的连接请求报文段突然又传送到了,因而产生 TCP 连接建立错误。

TCP的连接建立要解决三个问题
  • 使TCP双方能够确知对方的存在。
  • 使TCP双方能够协商一些参数(如最大报文段长度、最大窗口大小、时间戳选项等)。
  • 使TCP双方能够对运输实体资源(如缓存大小、各状态变量、连接表中的项目等)进行分配和初始化。
连接过程

        上图中给出了两台要基于TCP进行通信的主机,其中一台主机中的某个应用进程主动发起TCP连接,称为TCP客户,另一台主机中被动等待TCP连接的应用进程称为TCP服务器。可以将TCP建立连接的过程比喻为“握手”。“握手”需要在TCP客户和服务器之间交换三个TCP报文段。最初,两端的TCP进程都处于关闭(CLOSED)状态。

  1. TCP服务器进程首先创建传输控制块(TCB),用来存储TCP连接中的一些重要信息(例如TCP连接表、指向发送和接收缓存的指针、指向重传队列的指针以及当前发送和接收序号等)。之后,TCP服务器进程就进入监听(LISTEN)状态,等待TCP客户进程的连接请求。由于TCP服务器进程是被动等待来自TCP客户进程的连接请求,而不是主动发起的,因此称为被动打开连接
  2. TCP客户进程也要首先创建传输控制块,之后在打算建立TCP连接时向TCP服务器进程发送TCP连接请求报文段,并进入同步已发送(SYN-SENT)状态。TCP连接请求报文段首部中的同步标志位(SYN)被设置为 1,表明这是一个TCP连接请求报文段;序号字段seq被设置了一个初始值x。请注意,TCP规定SYN被设置为1的报文段不能携带数据,但要消耗掉一个序号。换句话说,TCP请求报文段不能携带数据(没有数据载荷),但是会消耗掉序号x。因此,TCP客户进程下一次发送的TCP报文段的数据载荷的第一个字节的序号为x+1。由于TCP连接是由TCP客户进程主动发起的,因此称为主动打开连接。( SYN = 1,seq = x)
  3. TCP服务器进程收到TCP连接请求报文段后,如果同意建立连接,则向TCP客户进程发送TCP连接请求确认报文段,并进入同步已接收(SYN-RCVD)状态。该报文段首部中的SYN和ACK都设置为1,表明这是一个TCP连接请求确认报文段;序号字段seq被设置了一个初始值y,这是TCP服务器进程所选择的初始序号。确认号字段ack的值被设置为x+1,这是对TCP客户进程所选择的初始序号的确认。请注意,TCP连接请求确认报文段也不能携带数据,但也要消耗掉一个序号,因为它也是SYN被设置为1的报文段。(  SYN = 1,ACK=1,seq = y,ack= x+1)
  4. TCP客户进程收到TCP连接请求确认报文段后,还要向TCP服务器进程发送一个普通的TCP确认报文段,并进入连接已建立(ESTABLISHED)状态。该报文首部中的ACK被设置为1,表明这是一个普通的TCP确认报文段;序号字段seq被设置为x+1,这是因为TCP客户进程发送的第一个TCP报文段(TCP连接请求报文段)的序号为x,虽然不携带数据,但要消耗掉一个序号,因此TCP客户进程发送的第二个报文段的序号为x+1。请注意TCP规定普通的TCP确认报文段可以携带数据,但如果不携带数据,则不消耗序号。换句话说,如果该报文段不携带数据,则TCP客户进程所发送的下一个数据报文段的序号仍是x+1。该普通确认报文段首部中的确认号字段ack被设置y+1,这是对TCP服务器进程所选择的初始序号的确认。( ACK=1,seq = x+1,ack= y+1)
  5. TCP服务器进程收到针对TCP连接请求确认报文段的普通确认报文段后,也进入连接已建立(ESTABLISHED)状态。此时,TCP双方都进入了连接已建立状态,它们可以基于已建立的TCP连接进行可靠的数据传输了。

问:为什么TCP客户进程最后还要发送一个普通的TCP确认报文段呢?这是否多余?或者说“能否使用“两报文握手”建立TCP连接?”

答:不能简化为“两报文握手”建立TCP连接,原因如下:

(1)TCP客户进程发出一个TCP连接请求报文段。但该报文段在某些网络节点长时间滞留了。这必然会造成该报文段的超时重传,假设重传的连接请求报文段被TCP服务器进程正常接收。
(2) TCP服务器进程给TCP客户进程发送一个TCP连接请求确认报文段,并进入连接已建立状态。请读者注意,由于现在改为“两报文握手”,因此TCP服务器进程发送完TCP连接请求确认报文段后,进入的是连接已建立状态,而不像“三报文握手”那样,进入同步已接收状态,并等待TCP客户进程发来针对TCP连接请求确认报文段的普通确认报文段。
(3)TCP客户进程收到TCP连接请求确认报文段后,进入TCP连接已建立状态。但不会给TCP服务器进程发送针对该报文段的普通确认报文段。
(4)现在,TCP双方都处于连接已建立状态,它们可以相互传输数据了。之后,可以通过“四报文挥手”来释放连接,TCP双方都进入了关闭状态。
(5)一段时间后,之前滞留在网络中的那个失效的TCP连接请求报文段,到达了TCP服务器进程。TCP服务器进程会误认为这是TCP客户进程又发起了一个新的TCP连接请求。
于是给TCP客户进程发送TCP连接请求确认报文段,并进入连接已建立状态。
(6) TCP客户进程收到TCP连接请求确认报文段后,由于TCP客户进程并没有发起新的TCP连接请求并且处于关闭状态,因此不会理会该报文段。
(7)然而,TCP服务器进程已进入连接已建立状态,它认为新的TCP连接已建立好了,并一直等待TCP客户进程发来数据,这将白白浪费TCP服务器进程所在主机的很多资源。
综上所述,采用“三报文握手”而不是“两报文握手”来建立TCP连接,是为了防止已失效的TCP连接请求报文段突然又传送到了TCP服务器进程,因而导致错误。

“四报文挥手”释放TCP连接

 释放过程

(1)数据传输结束后,TCP通信双方都可以释放TCP连接。现在TCP客户进程和TCP服务器进程都处于连接已建立(ESTABLISHED)状态。
(2)假设使用TCP客户进程的应用进程通知其主动关闭TCP连接,TCP客户进程会发送TCP连接释放报文段,并进入终止等待1(FIN-WAIT-1)状态。该报文段首部中的终止标志位(FIN)ACK的值都被没置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认:序号字段seq的值设置为u,它等于TCP客户进程之前已经传送过的数据的最后一个字节的序号加1。请注意,TCP规定FIN等于1的TCP报文段即使不携带数据,也要消耗掉一个序号。确认号字段ack的值设置为v,它等于TCP客户进程之前已收到的数据的最后一个字节的序号加1。(FIN= 1,seq = u)
(3)TCP服务器进程收到TCP连接释放报文段后,会发送一个普通的TCP确认报文段并进入关闭等待(CLOSE-WAIT)状态。该报文段首部中的ACK的值被设置为1,表明这是一个普通的TCP确认报文段;序号字段seq的值被设置为v,它等于TCP服务器进程之前已传送过的数据的最后一个字节的序号加1,这也与之前收到的TCP连接释放报文段中的确认号匹配。确认号字段ack的值被设置为u+1,这是对TCP连接释放报文段的确认。TCP服务器进程这时应通知高层应用进程:“TCP客户进程要断开与自己的TCP连接”。此时,从TCP客户进程到TCP服务器进程这个方向的连接就释放了。这时的TCP连接属于半关闭状态,也就是TCP客户进程已经没有数据要发送了,但TCP服务器进程如果还有数据要发送,TCP客户进程仍要接收,也就是从TCP服务器进程到TCP客户进程这个方向的连接并未关闭。半关闭状态可能会持续一段时间。(ACK = 1,seq = v ,ack = u+1)
(4)TCP客户进程收到该普通的TCP确认报文段后就进入终止等待2(FIN-WAIT-2)状态,等待TCP服务器进程发出的TCP连接释放报文段。若使用TCP服务器进程的应用进程已经没有数据要发送了,应用进程就通知其TCP服务器进程释放连接。由于TCP连接释放是由TCP客户进程主动发起的,因此TCP服务器进程对TCP连接的释放称为被动关闭连接。
(5)TCP服务器进程发送TCP连接释放报文段并进入最后确认(LAST-ACK)状态。该报文段首部中的FIN和ACK的值都被设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认。现在假定序号字段seq的值为w,这是因为在半关闭状态FTCP服务器进程可能又发送了一些数据。确认号字段ack的值u+1,这是对之前收到的TCP连接释放报文段的重复确认。(FIN = 1,ACK = 1,seq = w ,ack = u+1)
(6)TCP客户进程收到TCP连接释放报文段后,必须针对该报文段发送普通的TCP确认报文段,之后进入时间等待(TIME-WAIT)状态。该报文段首部中的ACK的值被设置为1,表明这是一个普通的TCP确认报文段;序号字段seq的值设置为u+1,这是因为TCP客户进程之前发送的TCP连接释放报文段虽然不携带数据,但要消耗掉一个序号。确认号字段ack的值设置为w+1,这是对所收到的TCP连接释放报文段的确认。(ACK = 1,seq = u+1 ,ack = w+1)
(7)TCP服务器进程收到该普通的TCP确认报文段后就进入关闭(CLOSED)状态,TCP服务器进程撤销相应的传输控制块。而TCP客户进程还要经过2MSL后才能进入关闭(CLOSED)状态。MSL的意思是最长报文段寿命(Maximum Segment Lifetime),[RFC793]建议为2分钟。也就是说,TCP客户进程进入时间等待(TIME-WAIT)状态后,还要经过4分钟才能进入关闭(CLOSBD)状态。这完全是从工程上来考虑的。对于现在的网络,MSL取为2分钟可能太长了,因此TCP允许不同的实现可根据具体情况使用更小的MSL值。经过2MSL时间后,TCP客户进程撤销相应的传输控制块后,就结束了这次的TCP连接。
        为什么TCP客户进程在时间等待(TIME-WAIT)状态必须等待2MSL的时间呢?借助下图进行说明

 等待2MSL的必要性

(1)TCP服务器进程发送TCP连接释放报文段后进入最后确认(LAST-ACK)状态。
(2)TCP客户进程收到该报文段后,发送普通的TCP确认报文段并进入关闭(CLOSED)状态而不是时间等待(TIME-WAIT)状态。
(3)然而,该TCP确认报文段丢失了,这必然会造成TCP服务器进程对之前所发送的TCP连接释放报文段的超时重传,并仍处于最后确认(LAST-ACK)状态。
(4)重传的TCP连接释放报文段到达TCP客户进程,由于TCP客户进程处于关闭(CLOSED)状态,因此不理睬该报文段,这必然会造成TCP服务器进程反复重传TCP连接释放报文段,并一直处于最后确认(LAST-ACK)状态而无法进入关闭(CLOSED)状态。
综上所述,处于时间等待(TIME-WAIT)状态后要经过2MSL时长,可以确保TCP服务器进程能够收到最后一个TCP确认报文段而进入关闭(CLOSED)状态。
另外,TCP客户进程在发送完最后一个TCP确认报文段后,再经过2MSL时长,就可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的TCP连接中不会出现旧链接中的报文段。

TCP保活计时器

        除时间等待计时器 (2MSL计时),TCP还设有一个保活计时器(Keepalive Timer)
设想下图所示的情况,TCP双方已经建立了连接。后来,TCP客户进程所在的主机突然出现了故障。显然,TCP服务器进程以后就不能再收到TCP客户进程发来的数据。因此,应当有措施使TCP服务器进程不要再白白等待下去。即用来防止TCP连接出现长时间空闲。

 TCP服务器进程解决上述问题的方法是使用保活计时器,具体如下:

  • TCP服务器进程每收到一次TCP客户进程的数据,就重新设置并启动保活计时器(通常为2小时)。
  • 若保活计时器在定时周期内未收到TCP客户进程发来的数据,则当保活计时器时后,TCP服务器进程就向TCP客户进程发送一个探测报文段,以后则每隔75秒发送一次。若一连发送10个探测报文段后仍无TCP客户进程的响应,TCP服务器进程就认为TCP客户进程所在主机出了故障,于是就关闭这个连接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值