目录
一、运输层
1. 概述
2. 运输层和网络层的关系
3. 运输层协议概述
二、多路复用和多路分解
1. 综述
2. 无连接的多路复用与多路分解(UDP)
3. 面向连接的多路复用与多路分解(TCP)
4. Web 服务器与TCP
三、UDP(无连接运输)
1. 综述
2. UDP 报文段结构
四、TCP(面向连接的运输)
1. TCP 连接概述
2. TCP报文结构
3. TCP 连接管理
(1)连接建立
(2)连接断开
(3)客户端状态转换
(4)服务器端状态转换
一、运输层
运输层位于 应用层 和 网络层之间,是分层的 网络体系结构的 重要部分。该层 为 运行在不同主机上的 应用进程 提供直接的 通信服务起着 至关重要的作用。
1. 概述
运输层 协议为运行 在不同主机上 的应用 进程之间提供了 逻辑通信(logic communica-tion)功能。
从应用程序的 角度看,通过 逻辑通信,运行 不同 进程的 主机好像 直接相连一样;实际上,这些 主机也许 位于 地球的两侧,通过 很多路由器及 多种不同 类型的 链路相连。
应用进程 使用 运输层提供的 逻辑通信功能 彼此 发送报文,而 无须考虑承载 这些报文的 物理基础设施的 细节。
运输层协议是在 端系统中 而不是在 路由器中实现的。
在发送端,运输层将从 发送应用程序进程 接收到的报文转换成 运输层 分组,用 因特网 术语来讲该 分组称为 运输层 报文段(segment)。实现的方法(可能)是将 应用报文 划分为 较小的块,并每块 加上一个 运输层首部以 生成运输层 报文段。
在 发送端 系统中,运输层 将这些报文段 传递给 网络层,网路层 将其封装成 网络层分组(即数据报)并向 目的地 发送。
在接收端,网络层 从数据报中 提取运输层 报文段,并将 该报文段向 上交给运输层。运输层则 处理接收到的 报文段,使该 报文段中的数据 为接收应用 进程使用。
网络应用程序 可以 使用多种的 运输层协议,每种 协议都能为 调用的应用程序 提供一组 不同的 运输层服务。
注意:
网络 路由器仅 作用于该 数据报的 网络层字段;即 它们 不检查封装在 该数据报的 运输层 报文段的字段。
2. 运输层和网络层的关系
在 协议栈中,运输层 刚好位于 网络层之上。网络层 提供了主机之间的 逻辑通信,而 运输层为 运行在不同主机上的 进程之间 提供了 逻辑通信。
这里用一个 家庭类比来 帮助分析 这种差别。
考虑有两个家庭,一家位于美国东海岸,一家位于美国西海岸,每家有12个孩子。东海岸家庭的孩子们是西海岸家庭孩子们的堂兄弟姐妹。
这两个家庭的孩子们喜欢彼此通信,每个人每星期要互相写一封信,每封信都用单独的信封通过传统的邮政服务传送。因此,每个家庭每星期向另一家发送144 封信。
每一个家庭有个孩子负责收发邮件,西海岸家庭是 Ann 而东海岸家庭是 Bill。每星期 Ann 去她的所有兄弟姐妹那里收集信件,并将这些信件交到每天到家门口来的邮政服务的邮车上。当信件到达西海岸家庭时,Ann 也负责将信件分发到她的兄弟姐妹手上。在东海岸家庭中的Bill 也负责类似的工作。
在这个例子中,邮政服务为两个家庭间提供逻辑通信,邮政服务将信件从一家送往另一家,而不是从一个人送往另一个人。在另一方面,Ann 和Bill 为堂兄弟姐妹之间提供了逻辑通信,Ann 和 Bill 从兄弟姐妹那里收取信件或到兄弟姐妹那里 交付信件。注意到从堂兄弟姐妹们的角度来看,Ann 和 Bill 就是邮件服务,尽管他们只是端到端交付过程的一部分(即端系统部分)。
- 应用层报文 = 信封上的字符
- 进程 = 堂兄弟姐妹
- 主机(又称为端系统)= 家庭
- 运输层协议 = Ann 和 Bill
- 网络层协议 = 邮政服务(包括邮车)
值得注意的是,Ann 和 Bill 都是在各自家里进行工作的;例如,他们并 没有参与 任何一个中间邮件中心 对邮件进行分拣,或者 将邮件 从一个邮件中心 送到另一个 邮件中心之类的工作。类似地,运输层协议 只工作在 端系统中。
在 端系统中,运输层协议 将来自 应用进程的报文移动到 网络边缘(即网络层),反过来也是一样,但对 有关这些报文 在网络核心如何移动 并不作任何规定。
事实上,中间 路由器 既不处理 也不识别运输层加在 应用层报文的 任何信息。
现在假定 Ann 和 Bill 外出度假,另外一对堂兄妹(如 Susan 和 Harvey)接替他们的工作,在家庭内部进行信件的收集和交付工作。
不幸的是,Susan 和 Harvey 的收集和交付工作与 Ann 和 Bill 所做的并不完全一样。由于年龄更小,Susan 和 Harvey 收发邮件的次数更少,而且偶尔还会丢失邮件(有时是被家里的狗咬坏了)。因此,Susan 和 Harvey 这对堂兄妹并没有提供与 Ann 和 Bill一样的服务集合(即相同的服务模型)。
与此类似,计算机网络中 可以 安排多种 运输层协议,每种协议为 应用程序 提供不同的 服务模型。Ann 和 Bill 所能提供的 服务明显 受制于 邮政服务所能 提供的服务。例如,如果 邮政服务不能提供在 两家之间 传递邮件 所需时间的 最长期限(例如3天),那么 Ann 和 Bill 就 不可能 保证邮件 在堂兄弟姐妹 之间 传递信件的 最长期限。
与此类似,运输协议 能够 提供的服务 常常 受制于 底层网络层协议的 服务模型。如果 网络层协议 无法为 主机之间 发送的 运输层报文 段提供 时延或 带宽保证的话,运输层 协议 也就 无法为 进程之间发送的 应用程 序报文提供 时延或 带宽保证。
然而,即使 底层网络协议 不能在网络层提 供相应的 服务,运输层协议 也能 提供 某些服务。例如,即使 底层网络协议 是不可靠的,也 就是说网络层协议 会使 分组丢失、篡改和冗余,运输协议 也能为 应用程序提供 可靠的 数据传输服务。
另一点是,即使 网络层 不能保证运输层报文段的 机密性,运输协议 也能使用 加密 来确保 应用程序报文 不被人侵者读取。
3. 运输层协议概述
因特网为 应用层提供了 两种截然不同的 可用运输层协议。这些协议一种是 UDP(用户数据报协议),它为 调用它的 应用程序 提供了一种 不可靠、无连接的服务。另一种是 TCP(传输控制协议),它为 调用它的应用程序 提供了一种 可靠的、面向连接的 服务。
当设计一个 网络应用程序时,该 应用程序的 开发人员在 生成套接字时 必须指定是 选择 UDP 还是选择 TCP。
我们将 运输层分组称 报文段(segment)。将 UDP 的分组称为数据报(data-gram)。
因特网网络层协议有一个名字叫 IP,即 网际协议。IP 为主机之间 提供了 逻辑通信。IP 的服务模型是 尽力而交付服务(best-effort delivery serv-ice)。这意味着 IP 尽它 “ 最大的努力 ” 在通信的主机之间 交付报文段,但 它并不做 任何确保。特别是,它不确保报文段的交付,不保证报文段的按序交付,不保证报文段中数据的完整性。由于这些原因,IP 被称 不可靠服务(unreliable service)。每台主机 至少有一个 网络层地址,即所谓的 IP 地址。
UDP 和 TCP 最基本的 责任是,将两个端系统间IP 的交付服务扩展为运行在端系统上的两个进程之间的交付服务。将主机间交付 扩展到 进程间交付 被称 运输层的多路复用(transport-layer multiplexing)与多路分解(demultiplexing)。UDP 和 TCP 还可以 通过在 其报文段 首部中包括 差错检查字段 而提供 完整性 检查。
进程 到 进程的 数据交付 和 差错检查是 两种最低限度的 运输层服务,也是 UDP 所能提供的仅有的两种服务。特别的,与 IP 一样,UDP 也是一种 不可靠的服务,即 不能保证一 个进程 所发送的数据 能够完整无缺地 到达目的 进程。
另一方面,TCP 为应用程序提供了 几种 附加服务。首先,它提供 可靠数据传输(relia-ble data transfer)。通过 使用流量控制、序号、确认和定时器,TCP 确保正确地、按序地将数据从发送进程 交付给 接收进程。这样,TCP 就将两个 端系统间的不可靠 IP 服务转换成了 一种 进程间的 可靠数据传输服务。
TCP 还提供 拥塞控制(con-gestion control)。拥塞控制 与其说是一种提供给调用它的应用程序的服务,不如说是一种 提供给整 个因特网的 服务,这是一种 带来通用 好处的服务。不太严格地说,TCP 拥塞控制 防止任何一条 TCP 连接用过 多流量来 淹没 通信主机 之间的 链路 和 交换设备。TCP 力求为 每个通过一条 拥塞网络链路的 连接平等地 共享 网络链路 带宽。这 可以 通过调节 TCP 连接的 发送端发送 进网络的 流量速率 来做到。
另一方面,UDP 流量是 不可调节的。使用 UDP 传输的应用程序可以根据 其需要 以其 愿意的任何 速率发送 数据。
二、多路复用和多路分解
1. 综述
在 目的主机,运输层 从紧邻其下的 网络层 接收报文段。运输层 负责将 这些报文段中的 数据交付 给在主机上 运行的适当应用 程序进程。
再来看一个例子。假定你正坐在计算机前下载 Web 页面,同时还在运行一个 FTP 会话 和 两个 Telnet 会话。这样 就有 4 个网络 应用进程 在运行,即两个 Telnet 进程,一个 FTP 进程 和 一个 HTTP 进程。当你的 计算机中的 运输层 从底层的网络层 接收数据时,它需要 将所接收到的 数据定向到这 4 个进程中的一个。现在来 研究这是 怎样完成的。
一个进程(作为网络应用的一部分)有一个 或 多个套接字(socket),它 相当于 从网络向 进程 传递数据 和 从进程向网络传递 数据的 门户。因此,在 接收主机中的 运输层 实际上并 没有直接 将数据 交付给进程,而是 将数据 交给了一个 中间的套接字。
由于 在任一时刻,在 接收主机上 可能有 不止一个套接字,所以 每个套接字都有 唯一的标识符。标识符 的格式 取决于它是 UDP 还是 TCP 套接字。
现在 考虑 接收主机 怎样将一个 到达的 运输层报文段 定向到 适当的 套接字。为此目的,每个运输层报文段中 具有 几个字段。在接收端,运输层 检查 这些字段,标识出 接收套接字,进而将 报文段定向到 该套接字。
将 运输层报文段中的 数据交付到 正确的套接字的 工作称为 多路分解(demultiplexing)。
在源主机 从不同套接字中 收集数据块,并为 每个数据块 封装上 首部信息(这将在 以后用于分解)从而 生成报文段,然后 将报文段传递到 网络层,所有 这些 工作称为多路复用(multiplexing)。
上图 中间的 那台主机的 运输层必须 将 从其下的网络层收到的 报文段分解 后交给 其上的 P1 或 P2 进程;这一过 程是 通过将 到达的报文段 数据 定向到对应 进程的套接字 来完成的。
中间主机中的 运输层也 必须收集 从这些套接字 输出的 数据,形成 运输层 报文段,然后 将其向下传 递给 网络层。
那它们在 主机中 实际是怎样工作的 ?从上述中运输层多路复用要求为:① 套接字有唯一标识符;② 每个报文段有特殊字段来 指示该报文段 所要交付到的 套接字。
这些特殊字段是 源端口号字段(source port number field)和 目的端口号字段(destination port number field)。端口号 是一个 16 比特的数,其大小在 0~65535 之间。
0~1023 范围的端口号称为 周知端口号(well-known portnumber),是受限制的,这是指它们 保留给诸如 HTTP(它使用端口号 80)和 FTP(它使用端口号 21)之类的周 知应用层 协议来使用。当我们 开发一个新的 应用程序时,必须为 其分配一个 端口号。
2. 无连接的多路复用与多路分解(UDP)
(1)当我们使用 socket()函数 创建一个 UDP 套接字时,运输层 自动地为该 套接字 分配一个 端口号。特别是,运输层 从范围 1024~65535 内分配一个 端口号,该 端口号 是当前 未被该主机 中 任何其他 UDP 端口 使用的 号。
(2)另外一种方法是,在 创建一个 套接字 后,我们 能够 通过套接字 bind()方法 为这个 UDP 套接字 关联一个 特定的 端口号。
(3)如果 应用程序开发者 所编写的 代码实现 的是一个 “ 周知协议 ” 的服务器端,那么 开发者 就必须 为其分配 一个相应的 周知 端口号。
通常,应用程序的客户端 让运输层自动地(并且是透明地)分配 端口号,而 服务器端 则分配一个特 定的端口号。
假定在 主机 A 中的一个 进程 具有 UDP 端口 19157,它要 发送一个 应用程序 数据块 给 位于 主机 B 中的 另一进程,该进程 具有 UDP 端口 46428。
主机 A 中的 运输层 创建一个 运输层 报文段,其中 包括应用 程序数据、源端口号(19157)、目的 端口号(46428)和 两个其他值。
然后,运输层 将得到的 报文段 传递到 网络层。网络层 将该报文段 封装到一个 IP 数据报中,并 尽力而为地 将报文段 交付给接 收主机。
如果该 报文段到达接收 主机 B,接收主机 运输层 就检查该 报文段中的 目的 端口号(46428)并将 该报文段交 付给 端口号 46428 所标识的 套接字。
注意:
① 主机 B 能够 运行多个进程,每个 进程有自己的 UDP 套接字 及 相应的端 口号。当UDP 报文段 从 网络 到达时,主机 B 通过 检查该 报文段中的 目的端口号,将 每个报文段 定向(分解)到 相应的 套接字。
② 一个 UDP 套接字是 由一个 二元组 全面标识的,该 二元组 包含一个 目的 IP 地址和一个 目的 端口号。因此。如果两个 UDP 报文段 有不同的 源 IP 地址 或源端口号,但 具有相同的目的 IP 地址 和 目的端口号,那么这 两个报文 段 将通 过相同的 目的套接字 被 定向到 相同的 目的进程。
源端口号的 用途 是什么呢?
在 A 到 B 的报文段 中,源端口号 用作 “ 返回地址 ” 的一部分,即当 B 需要 回发一个 报文段给 A 时,B 到 A 的报文段 中的 目的端口号 便从 A 到 B 的报文段 中的 源端口号中 取值。(完整的返回地址是 A 的 IP 地址 和 源端口号。)
3. 面向连接的多路复用与多路分解(TCP)
TCP 套接字 和 UDP 套接字 之间的一个 细微差别是,TCP 套接字 是由 一个 四元组(源 IP 地址,源端口号,目的 IP 地址,目的端口号)来标识的。因此,当一个 TCP 报文段 从 网络 到达一台主机 时,该主机 使用 全部 4 个值 来将 报文段定向(分解)到 相应的套接字。
与 UDP 不同的是,两个具有 不同源 IP 地址 或 源端口号的 到达 TCP 报文段 将被 定向到 两个不同的 套接字,除非 TCP 报文段 携带了初 始创建连接的 请求。
举个 TCP 客户-服务区编程 的例子:
- TCP服务器应用程序有一个 “ 欢迎套接字 ” ,它在 12000 号 端口上 等待来自 TCP 客户的 连接 建立请求。
- TCP 客户使用 socket()函数 创建一个 套接字并 使用 connect()函数 发送一个 连接 建立请求 报文段。
- 一条 连接建立 请求 只不过 是一个 目的端口号 为 12000,TCP 首部的 特定 “ 连接建立位 ” 置位的 TCP 报文段。这个 报文段 也包含 一个由 客户选择 的 源端口号。
- 当 运行服务器 进程的 计算机 的主机操作系统 接收到 具有 目的端口 12000 的人连 接请求 报文段后,它就 定位 服务器进程,该进程 正在 端口号 12000 等待 接受连接。该 服务器 进程则 创建一个新的 客户端套接字。
- 该 服务器的 运输层还 注意到 连接请求报文段 中的 下列 4 个值:① 该报文段中的源端口号;② 源主机IP 地址;③ 该报文段中的 目的端口号;④ 自身的 IP 地址。新 创建的连接 套接字通过这 4 个值来 标识。所有 后续到达 的 报文段,如果 它们的 源端口号、源主机 IP 地址、目的端口号 和 目的 IP 地址 都与这 4 个值匹配,则 被分解到 这个套接字。
- 随着 TCP 连接 完成,客户 和 服务器 便可相 互发送 数据了。
服务器主机 可以 支持很多 并行的 TCP 套接字,每个 套接字 与 一个进程 相联系,并 由 其四元组 来标识 每个套接字。
当一个 TCP 报文段 到达主机 时,所有 4 个字段(源 IP 地址,源端口,目的 IP 地址,目的端口)被用来 将报文段 定向(分解)到相应的 套接字。
上图中 主机 C 向 服务器 B 发起了两个 HTTP 会话,主机 A 向 服务器 B 发起了一个 HTTP 会话。主机 A 与主机 C 及 服务器 B 都有自己 唯一的 IP 地址,它们 分别是 A、C、B。
主机 C 为其两个 HTTP 连接 分配了 两个 不同的 源端口号(26145 和 7532)。因为 主机 A 选择 源端口号时 与主机 C 互不相干,因此 它也可以 将源端口号 26145 分配给其 HTTP 连接。服务器 B 仍然能够 正确地 分解 这两个 具有相同 源端口号 的连接,因为这 两条连接 有不同的 源 IP 地址。
4. Web 服务器与TCP
考虑一台运行 Web 服务器的主机,例如在 端口 80 上运行一个 Apache Web 服务器。
当客户(如浏览器)向 该服务器 发送报文段 时,所有 报文段的 目的端口都 将为 80。特别 是,初始连接 建立报文段 和 承载 HTTP 请求的 报文段都有 80 的 目的端口。该服务器 能够 根据源 IP 地址 和 源端口号 来 区分来自不同客户的 报文段。
上图显示了一台 Web 服务器 为 每条连接 生成一个 新进程。每个 这样的 进程 都有 自己的 连接 套接字,通过这些套接字可以收到 HTTP 请求 和 发送 HTTP 响应。
然而,连接 套接字 与 进程之间 并非 总是有着 一一对应的 关系。事实上,当今 的 高性能 Web 服务器 通常只 使用一个进程,但是为 每个新的客户连接 创建一个 具有 新连接套接字的 新线程。对于 这样一台服务器,在 任意给定的 时间内都 可能有(具有不同标识的)许多 连接 套接字连接到 相同的进程。
如果 客户 与 服务器 使用 持续 HTTP,则 在整条 连接持续 期间,客户 与 服务器 之间经由 同一个服务器 套接字 交换 HTTP 报文。
然而,如果 客户 与 服务器 使用 非持续 HTTP,则对 每一对 请求 / 响应 都创建一个 新的 TCP连接并在随后关闭,因此 对每一对 请求 / 响应 创建一个 新的套接字 并在 随后关闭。这种 套接字的 频繁创建 和 关闭会 严重地 影响一个 繁忙的 Web 服务器 的性能(尽管有 许多 操作 系统技巧可用来 减轻这个 问题的影 响)。
三、UDP(无连接运输)
1. 综述
UDP 只是做了 运输协议 能够做的 最少工作。除了 复用 / 分解功能 及 少量的 差错检测 外,它几乎没有对 IP 增加别的 东西。
UDP 从应用 进程得到 数据,附加上 用于 多路复用 / 分解服务 的 源和目的 端口号字段,以及 两个其他的 小字段,然后 将形成的 报文段交给 网络层。网络层将 该运输层报文段 封装 到一个 IP 数据报中,然 后尽力而为地 尝试将此报文段 交付给 接收主机。如果 该报文段 到达接收 主机,UDP 使用目的端口号 将报文段中 的数据 交付给正确的 应用进程。
值得注意的是,使用 UDP 时,在 发送报文段 之前,发送方 和 接收方的 运输层 实体 之间 没有握手。正因为如此,UDP 被称为是无连接的。
DNS 是一个通常使用 UDP 的应用层协议的例子。
当一台 主机中的 DNS 应用程序 想要进行 一次 查询时,它 构造了一个 DNS 查询报文 并将其交给 UDP。无须 执行任何 与 运行在目的端系统 中的 UDP 实体 之间的握手,主机端的 UDP 为 此报文添加 首部字段,然后 将形成的报文段 交给 网络层。
网络层 将此 UDP 报文段 封装进一个 IP 数据报 中,然后 将其 发送给一个 名字 服务器。在 查询主机中的 DNS 应用程序 则 等待对 该查询的 响应。
如果 它没有 收到 响应(可能是由于底层网络丢失了查询或响应),则要么试图向 另一个 名字服务器 发送 该查询,要么 通知调用 的 应用程序它 不能 获得响应。
2. UDP 报文段结构
应用层 数据 占用 UDP 报文段的数据字段。例如,对于 DNS应用,数据字段要么 包含一个 查询 报文,要么 包含一个响应报文。
UDP 首部只有 4 个字段,每个 字段由 两个字节组成。通过 端口号可以 使目的 主机将 应用数据交给 运行在 目的端系统中 的 相应进程(即 执行 分解功能)。
长度字段 指示了在 UDP 报文段中 的 字节数(首部 加数据)。因为 数据字段的 长度 在一个 UDP 段中 不同于在 另一个段中,故 需要一个 明确的长度。
接收方 使用检验和来 检查在该报文段中 是否出现了差 错。
四、TCP(面向连接的运输)
1. TCP 连接概述
TCP 被称为是 面向连接的(connection-oriented),这是因为在 一个应用 进程可以 开始向另一个应用进程发送数据之前,这 两个进程必须先相互 “ 握手 ” ,即 它们必须 相互发送 某些 预备报文段,以 建立确保数据传输的 参数。作为 TCP 连接建立的 一部分,连接的 双方 都将 初始化与 TCP 连接相关的 许多 TCP 状态变量。
这种 TCP “ 连接 ” 不是一条像在 电路交换网络中的 端到端 TDM 或 FDM 电路。相反,该 “ 连接 ” 是一条 逻辑连接,其 共同状态仅保留在 两个通信端系统的 TCP 程序中。由于 TCP 协议只在端系统 中运行,而 不在中间的 网络元素(路由器 和 链路层交换机)中运行,所以 中间的网络元素不会维持 TCP 连接状态。事实上,中间路由器对 TCP连接完会视而不见,它们看到的是数据报 而不是连接。
TCP 连接总是 点对点(point-to-point)的,即在 单个发送方 与 单个接收方之间的 连接。所谓 “多播”,即 在一次发送操作中,从 一个发送方 将 数据传送给 多个接收方,这种情况对 TCP 来说是不可能的。对于 TCP 而言,两台主机 是一对,而 3 台主机则太多了。
现在来看看 TCP 连接是怎样建立的。
假设运行在某台主机上的一个进程(客户进程)想与 另一台主机上的一个 进程(服务器进程)建立 一条连接。该 客户应用进程首先使用 connect()函数通知 客户运输层,它想 与 服务器上的 一个 进程建立一条连接。
客户上的 TCP 便开始 与 服务器上的 TCP 建立一条 TCP 连接。客户 首先 发送一个 特殊的TCP 报文段,服务器 用 另一个特殊的 TCP 报文段来 响应,最后,客户再 用第 三个特殊报文段 作为响应。
前两个报文段不承载 “ 有效载荷 ” ,即 不包含 应用层数据;而 第三个报文段 可以 承载有效 载荷。由于 在这两台主机之间 发送了 3 个报文段,所以 这种连接 建 立过程常被称为 三次握手(three-way handshake)。
一旦建立起一条 TCP 连接,两个应用 进程之间就 可以相互发送数据了。
现在考虑一下 从客户进程向 服务器进程 发送数据的情况。
客户进程 通过套 接字(该进程之门)传递数据流。数据 一旦 通过该门,它就由 客户中 运行的 TCP 控制了。如上图所示,TCP 将这些数据 引导到 该连接的 发送缓存(send buffer)里,发送缓存是 发起三次握手期间设置的 缓存之一。
接下来 TCP 就会不时从发送缓存里 取出一块 数据,并将 数据传递到 网络层。TCP 可从 缓存中取出 并放入 报文段中的 数据数量 受限于最大报文段长度(Maximum Segment Size,MSS)。MSS 通常 根据最初确定的 由本地 发送主机 发送的 最大链路层帧长度(即所谓的 最大传输单元(Maximum Transmission Unit, MTU))来设置。设置该 MSS 要保证一个 TCP 报文段(当封装在一个 IP 数据报中)加上 TCP/IP 首部长度(通常 40 字节)将适合 单个 链路层帧。
TCP 为每块客户数据配上一个 TCP 首部,从而形成多个 TCP 报文段(TCP segment)。这些报文段被 下传给网络层,网络层 将其 分别封装在 网络层 IP 数据报中。然后这些 IP 数据报被 发送到网络中。
当 TCP 在另一端 接收到一个 报文段后,该报文段的数据就被 放人该 TCP 连接的 接收缓存 中,如上图中所示。应用程序 从此缓存中 读取数据流。
2. TCP报文结构
TCP 报文段 由首部字段 和 一个数据字段 组成。
数据字段 包含一块 应用 数据。其中 MSS 限制了 报文段数据字段 的 最大长度。当 TCP 发送一个 大文件,TCP 通常是 将该文件 划分成 长度 MSS 的若干块(最后一块 除外,它通常 小于 MSS)。然而,交互式应用 通常 传送 长度小于 MSS 的 数据块。
与 UDP 一样,首部 包括源 端口号 和 目的 端口号,它 被用于 多路复用 / 分解 来自 或 送到上层 应用的数 据。另外,同 UDP一样,TCP 首部也 包括 检验 和 字段(checksum field)。
3. TCP 连接管理
(1)连接建立
来观察一下一条TCP 连接是如何建立的。假设运行在一台主机(客户)上的一个进程想与另一台主机(服务器)上的一个进程建立一条连接。
客户应用 进程 首先 通知客户 TCP,它想建立一个 与服务器上 某个进程之间的 连接。客户中的 TCP 会用 以下方式与服务器中的 TCP 建立一条 TCP 连接:
第一步:客户端的 TCP 首先向服务器端的 TCP 发送一个 特殊的 TCP 报文段。该报文段中不包含 应用层数据。但是在 报文段的首部中的一个标志位(即 SYN 比特)被置 1。因此,这个 特殊报文段被称 SYN 报文段。另外,客户会随机地 选择一个 初始序号(client_isn),并将 此编号放置于该起始的 TCP SYN 报文段的 序号字段中。该 报文段会被 封装在一个 IP 数据报 中,并发送给 服务器。
第二步:一旦包含 TCP SYN 报文段的 IP 数据报 到达服务器 主机(假定它的确到达了!),服务器会 从该数据报中 提取出 TCP SYN 报文段,为该 TCP 连接 分 配 TCP 缓存 和 变量,并向该客户 TCP 发送 允许连接的 报文段。这个允许 连接的 报文段 也不包含 应用层数据。但是,在 报文段的 首部却包含 3 个重要的 信息。首先,SYN 比特被置 为 1。其次,该 TCP 报文段首部的 确认号 字段 被置为 client_isn +1。最后,服务器 选择自己的 初始序号(server_isn),并将其放置到 TCP 报文段 首部的序号 字段中。这个 允许连接的 报文段 实际 上表明了:“ 我收到了你发起建立连接的 SYN 分组,该分组 带有 初始序号 client_isn。我 同意 建立该 连接。我 自己的 初始序号是 server_isn。” 该允许连接的 报文段被称为 SYNACK 报文段(SYNACK segment)。
第三步:在收到 SYNACK 报文段后,客户也要 给该连接 分配缓存 和 变量。客户主机 则 向服务器 发送另外一个 报文段;这 最后一个报文段 对服务器的 允许连接的 报文段 进行了 确认(该客户通过将值 server_isn +1 放置到 TCP 报文段首部的 确认字段中来 完成此项 工作)。因为连接已 经建立了,所以该 SYN 比特被 置 0。
该 三次握手的 第三个阶段 可以在报文段负载 中携 带客户到服务器的 数据。以后 每一个报文段中,SYN 比特都将被 置为 0。
(2)连接断开
参与一条 TCP 连接的 两个进程中的 任何一个 都能终止 该连接。当 连接结束后,主机 中的 “ 资源 ”(即缓存和变量)将 被释放。
举个例子,假设某客户打算关闭连接。
客户 应用进程 发出一个 关闭连接 命令。这会引起客户 TCP 向服务器 进程 发送一个 特殊的TCP 报文段。这个特的 报文段 让其首部中 的一个 标志位即 FIN 比特被 设置 1。
当服 务器接收到 该报文段 后,就向发送方回送一个 确认 报文段。然后,服务器发送它自己的 终止报文段,其 FIN 比特被置 为 1。
最后,该客户 对 这个服务器的 终止报文段 进行确认。此时,在 两台主机上 用于 该连接的 所有资源 都被 释放了。
(3)客户端状态转换
在一个 TCP 连接的 生命周期 内,运行在 每台主机中的 TCP 协议 在各种 TCP 状态(TCP state)之间变迁。
- 客户 TCP开始时处于 CLOSED(关闭)状态。
- 客户的 应用程序发起一个 新的 TCP 连接。这引起客户 中的 TCP 向服务器中的 TCP 发送一个 SYN 报文段。在发送过 SYN 报文段后,客户 TCP 进入了 SYN_SENT 状态。
- 当客户 TCP 处在 SYN_SENT 状态时,它 等待 来 自服务器 TCP 的 对 客户所发报文段 进行 确认且 SYN 比特被 置为 1 的一个 报文段。收到 这样 一个报文段 之后,客户 TCP 进入 ESTABLISHED(已建立)状态。
当处在 ESTABLISHED 状态时,TCP 客户就能发送 和接 收包含 有效载荷 数据(即 应用层产生的数据)的 TCP 报文段了。
假设客户 应用程序决 定要 关闭该连接。(服务器 也能 选择 关闭该连接)
- 这引起客户 TCP 发送一个带有 FIN 比特被 置为 1 的 TCP 报文段,并进入 FIN_WAIT_1 状态。
- 当处在 FIN_WAIT_1 状态时,客户 TCP 等待一个 来自服务器的 带有 确认的 TCP 报文段。
- 当 它收到该 报文段时,客户 TCP 进入 FIN_WAIT_2状态。
- 当处在 FIN_WAIT_2 状态时,客户 等待来自 服务器的 FIN 比特 被置为 1 的 另一个 报文段;当收到该 报文段 后,客户 TCP 对服务器的 报文段 进行确认,并进人 TIME_WAIT状态。
- 假定 ACK 丢失,TIME_WAIT 状态使 TCP 客户 重传最后的 确认报文。在 TIME_WAIT 状态中 所消耗的时间 是 与 具体实现 有关的,而典型的值是 30秒、1分钟 或 2分钟。经过 等待后,连接 就正式关闭,客户端 所有资源(包括端口号)将 被释放。
(4)服务器端状态转换
假设客户开始连接拆除。