我们的传输层协议主要负责数据能够从发送端传输接收端。
一. 传输层的意义
数据从应用层向下传递时,并不会直接交给网络硬件,而是沿着网络协议栈自顶向下逐层封装,依次经过传输层、网络层、数据链路层,最终由物理层硬件发送到网络中。
前面学习了应用层协议 HTTP 与 HTTPS:应用层负责定义应用之间的数据交互规则,HTTPS 才负责保障传输的数据安全;而传输层的核心作用,是为两端应用程序提供端到端的数据传输服务(可以选择可靠传输,也可以选择不可靠传输),保障数据能够送达目标主机上对应的应用进程。
二.再谈端⼝号
端⼝号(Port)标识了⼀个主机上进⾏通信的不同的应⽤程序;
1.五元组标识一个通信
五元组(源 IP、源端口、目的 IP、目的端口、传输层协议)唯一标识一条通信连接。主机上存在多个网络服务,数据包向上交付至传输层时,传输层解析目的端口号,从而确定数据交付给本机对应的应用进程。依靠五元组,操作系统可以区分多条并发通信,实现一台主机同时运行多个端口不同的网络服务。

在 TCP/IP 协议中,用 “源IP”,“源端口号”,“目的 IP”,“目的端口号”,“协议号” 这样一个五元组来标识一个通信。可以通过命令:netstat -nltp 进行查看:
命令:netstat -nltp 参数含义:
- -n:数字形式显示 IP、端口,不解析域名
- -l:只列出处于监听 LISTEN 状态的端口(正在等待客户端连接的服务)
- -t:只看 TCP 协议
- -p:显示对应的进程 PID 和程序名(需要 root 权限才能看到全部进程)

- 0.0.0.0:23000:监听本机所有网卡,外部其他机器可以访问这个端口
- 127.0.0.1:39999:本地回环地址,只允许本机内部程序访问,外部主机无法连接 39999 端口
- docker-prox:Docker 容器代理进程,你大量端口(23000、5601 等)都是 Docker 容器开放的服务
- sshd:SSH 远程连接服务,就是你用来登录这台 Ubuntu 虚拟机的服务
- code-645f29c:VS Code 服务,绑定127.0.0.1:39999,仅本机访问
- systemd-resolve:系统域名解析服务
举例:浏览器多标签页访问 CSDN,客户端源 IP 不变,但每个标签页使用不同源端口,形成不同五元组,服务器以此区分多条并发连接。
通信流程:数据包到达服务器,网络层校验目的 IP 确定主机;
传输层解析目的端口,定位本机目标应用进程,并识别传输层协议。生成响应报文时,将客户端报文的源 IP、源端口作为响应的目的 IP、目的端口,把响应数据回传给对应的客户端进程。

2.端口号范围划分
端口号是一个16 位无符号整数,取值范围为 0~65535,总共 65536 个端口。
- 0~1023:知名端口号(公认端口)。HTTP、FTP、SSH 这类广泛使用的应用层协议,会固定使用该区间内的端口。可以类比生活中的公共服务号码,如 110、120,是预先约定好的。
- 1024~65535:分为注册端口和动态 / 私有端口。客户端发起网络请求时,操作系统会从这个区间动态分配临时源端口;同时该区间也允许开发者手动绑定端口用于服务程序。
3.知名端口号(Well-Know Port Number)
部分网络服务使用频率极高,为了通信双方方便识别,行业预先约定好固定端口:
- SSH 远程登录服务:22 端口
- FTP 文件传输服务:21 端口(控制端口)
- Telnet 远程登录服务:23 端口
- HTTP 网页服务:80 端口
- HTTPS 加密网页服务:443 端口
命令:ps axj | head -1 && ps axj | grep sshd
命令作用
ps axj:查看系统全部进程,以完整字段格式输出;
head -1:打印表头;
&&:前面命令执行成功后,才执行后面;
grep sshd:筛选出名称包含 sshd 的进程。

sshd 是后台守护进程,监听 22 端口。每当有新的 SSH 客户端连接,主 sshd 进程会创建对应的子进程,一个子进程对应一条 SSH 通信连接。系统使用 PID 标识进程,配合传输层端口,完成网络数据和应用进程之间的分发。
查看端口号
可以通过命令 vim /etc/services 查看文件内容,该文件是记录网络服务名和它们对应使用的端口号及协议。

所以当我们自己写一个程序使用端口号时,要避开这些知名端口号。
4.绑定端口号数目问题
一个进程可不可以 bind(绑定)多个端口号?
可以。 单个进程能够同时绑定多个不同端口。例如服务程序同时监听 80 端口和 8080 端口,两个端口收到的数据都会交给这同一个进程处理。端口本质作用是把网络数据包分发到对应进程;多个端口指向同一个进程,不会破坏端口标识进程的逻辑。
一个端口号是否可以被多个进程 bind?
默认情况下不可以。 在常规 TCP 场景下,同一个主机的同一个端口,同一时间只能被一个进程绑定监听。如果尝试第二个进程绑定已经被占用的端口,程序会直接报错,提示端口已被占用。
5.pidof & netstat 命令
(1)netsate 命令
netstat 是一个用来查看网络状态的重要工具。
a.. 常用选项
n:拒绝显示别名,能显示数字的全部转化成数字。
l:仅列出有在 Listen(监听)的服务状态。
p:显示建立相关链接的程序名(进程:process)。
t(tcp):仅显示 tcp 相关选项。
u(udp):仅显示 udp 相关选项。
a(all):显示所有选项,默认不显示 LISTEN 相关。


命令:sudo netstat -nulp 参数说明
- -n:以数字形式显示 IP 地址与端口号,不解析域名和服务名称
- -u:只查看 UDP 协议
- -l:只查看处于监听的服务
- -p:显示占用端口的进程 PID 与程序名称,需要 sudo 权限
重点:UDP 是无连接协议,没有 TCP 中的 LISTEN 监听状态,所以 State 这一列是空的。
字段含义
(2)pidof 命令
是通过进程名来查看服务器进程的 pid。

结论:命令语法本身没错,但是当前系统没有 HttpServer 进程,所以出现这个提示,不是命令写错了
原理:
当目标进程已经终止,pidof无法获取进程号,管道传递空值给xargs,xargs直接执行kill -9,缺少进程 ID 参数,kill就输出帮助信息。这是一个很典型的实验现象:进程不存在时执行杀进程命令产生的报错,正好可以写进实验现象与分析部分。
三.UDP 协议
1.UDP 协议段格式

- 16 位源端口号:表示数据从哪里来。
- 16 位目的端口号:表示数据要到哪里去。
- 16 位 UDP 长度:表示整个数据报(UDP 首部 + UDP 数据)的最大长度。
- 16 位 UDP 检验和:如果UDP报文的检验和出错,就会直接将报文丢弃。
为什么我们在应用层编写网络代码时,定义端口号一般都选用 uint16_t?
根本原因是传输层协议规定端口号本身就是16 位无符号整数,取值范围 0~65535,正好匹配uint16_t的存储范围。
调用send发送数据时,数据并不会直接送入网络,而是先交给操作系统内核里的传输层去处理,由传输层完成协议封装。
如何分离 UDP 报文的报头与有效载荷?
UDP 属于固定长度报头,报头一共包含 4 个字段,每个字段占 16 位,总长度固定为 8 字节。解析报文时,直接读取报文最开始的 8 字节作为 UDP 报头,报文剩余部分就是 UDP 的有效载荷(应用层数据)。
UDP 是面向数据报的协议,它会保留应用层每次发送数据的边界,接收方每次读取,都会收到一个完整独立的 UDP 报文,不会把多个报文合并拆分。
UDP 如何完成数据交付?也就是怎么把有效载荷交付给上层对应的应用进程?
系统中每个正在运行的网络应用进程,会绑定一个端口号。当 UDP 报文到达主机,传输层读取 UDP 报头里的目的端口号,根据这个端口号找到对应的应用进程,把 UDP 报文里的有效载荷交给这个应用进程。
2.UDP 报文
其实看这里的报头就知道其实就是一种结构化数据对象(位段):
struct udp_hdr
{
uint16_t src_port; // 源端口
uint16_t dsc_port; // 目的端口
uint16_t udp_len; // UDP长度
uint16_t udp_check; // 校验和
};
(1)UDP 数据封装过程
我们要先知道应用层 sendto 数据是发给传输层的,看图:

过程:创建一块内存,计算出有效载荷的起始地址,拷贝有效载荷,强转填写报头部分,最后形成 UDP 报文。
(2)UDP 数据交付过程
UDP 报文到达目标主机后,操作系统内核的传输层模块开始处理报文。UDP 报头为固定 8 字节长度,可直接解析提取出目的端口号。内核通过这个目的端口号,在本机的端口映射表中找到绑定该端口的应用进程,剥离 UDP 报头,将报文里面的有效载荷向上交付给对应的应用进程。
3.UDP 的特点
UDP 的传输过程类似于寄信。
- 无连接:只需要知道对端的 IP 地址与端口号,就可以直接发送数据,不需要提前建立通信连接。
- 不可靠:没有应答确认机制,也没有丢包重传机制。如果报文在网络传输过程中丢失,UDP 协议不会向应用层反馈任何错误提示。
- 面向数据报:应用层读写数据的粒度由报文本身决定,无法自由拆分、合并读写。
面向数据报
举例理解:对方寄出三个独立快递,接收方就必须接收三次;不会自动合并成一个包裹,也不允许只收取包裹的一部分。如果只有一个包裹,接收时也不能只拿走一半货物。
结论:发送方发出一个完整 UDP 报文,接收方使用recvfrom读取时,要么不读取,要么一次性读取完整报文后返回。
应用层交给 UDP 多大长度的报文,UDP 就原样发送,不会对报文进行拆分,也不会将多个报文合并。 举个例子:使用 UDP 传输 100 字节数据。若发送端调用一次sendto发送 100 字节,接收端就必须对应调用一次recvfrom读取完整 100 字节;不能循环调用 10 次recvfrom,每次只读取 10 字节。
4.UDP 的缓冲区
- UDP 没有真正意义上的发送缓冲区。因为 UDP 不提供可靠传输机制,不需要对已发送数据进行暂存重传。应用层调用 sendto 发送数据时,内核直接将数据封装上 UDP 报头,交给网络层进行后续传输。
- UDP 具有接收缓冲区。该缓冲区用于暂存已经到达、但应用层尚未读取的 UDP 报文。但接收缓冲区不保证接收顺序与发送顺序一致,因为网络可能出现丢包、乱序。如果接收缓冲区已满,后续到达的 UDP 数据报会直接被丢弃。
UDP 的 socket 既能读,也能写,支持同时进行收发,因此 UDP 是全双工的。
为什么 UDP 要有接收缓冲区?
如果 UDP 没有接收缓冲区,就要求应用层必须实时、逐字地读取每一个到达的报文。一旦应用层暂时没来得及读取,后续从底层网卡上来的新报文就会被迫丢弃,应用层无法事后补读。接收缓冲区的作用就是在网络到达速度和应用层读取速度之间做一个缓冲解耦。
5.UDP 传输最大长度
UDP 报头中存在 16 位的报文总长度字段,因此单个 UDP 数据报的最大传输长度为 64KB,该大小包含 UDP 报头(8 字节)和应用层有效载荷。
除去固定 8 字节的 UDP 报头,单个 UDP 报文最多只能传输 65507 字节的应用数据。
在实际网络环境中,64KB 的传输上限较小。当应用层需要传输的数据超过 64KB 时,UDP 协议无法自动分包和重组,必须由开发者在应用层手动分包、多次发送,并在接收端手动拼接数据,完成大数据传输。
6.基于 UDP 的应用层协议
UDP 协议开销小、传输速度快、无需建立连接,适合对实时性要求高、可容忍少量丢包的场景。常见基于 UDP 实现的应用层协议如下:
除此之外,用户自行编写的 UDP 网络程序,自定义数据格式与通信规则,也属于自定义 UDP 应用层协议。
四.TCP 协议(传输控制协议)
TCP(Transmission Control Protocol),它要对数据的传输进行一个详细的控制。

1.TCP 协议段格式

(1)TCP 报头当中各个字段的含义
TCP 报头当中的 6 位标志位:
- URG:紧急指针是否有效,置 1 代表存在紧急数据,优先处理。
- ACK:确认序号是否有效,TCP 连接建立完成后,所有报文段 ACK 基本都置 1。
- PSH:提示接收端应用程序立刻从 TCP 缓冲区读取数据,不要继续缓存。
- RST:复位标志,代表要求对方重置连接;携带 RST 标识的报文称为复位报文段,常用于异常中断连接。
- SYN:同步标志,请求建立连接;携带 SYN 标识的报文称为同步报文段。
- FIN:结束标志,通知对端,本方数据发送完毕,准备关闭连接;携带 FIN 标识的报文称为结束报文段。
(2)4 位头部长度
该字段占 4 个比特位,仅从二进制数值来看,可表示范围为【0~15】。TCP 协议规定,该字段的单位为 4 字节,最终报头总长度 = 头部长度数值 × 4 字节。 TCP 报头最小长度为 20 字节,因此该字段最小取值为 5;最大取值为 15,对应报头最大 60 字节。因此 4 位首部长度的有效取值范围是【5~15】,对应二进制【0101~1111】。 TCP 基础固定报头占 20 字节,报头超出 20 字节的部分为选项字段,因此选项字段最大长度为 40 字节。
TCP 如何将报头与有效载荷进行分离?
TCP 报头是变长的,最小 20 字节,最大 60 字节。解析步骤如下:
有效载荷如何向上交付?
应用层每个进程都会绑定一个端口号:服务端通过 bind 显式绑定端口号,客户端由操作系统从动态端口范围内自动分配端口号。 TCP 报头中包含目的端口号,内核通过这个端口号在本机的端口映射表中查找对应的应用进程,将有效载荷向上交付给该进程。
补充:内核中使用哈希表维护端口号与进程(socket 文件描述符)之间的映射关系,因此传输层可以通过目的端口号快速定位到对应的应用进程。映射关系的建立时机是在应用进程调用 bind 绑定端口号的时候。
(3)TCP 报头
和前面 UDP 报头的封装思路类似,TCP 报头本质也是一段结构化的数据。在内核封装 TCP 报文段时,会先申请一块连续内存空间;内存空间的前一部分用来存放 TCP 报头,通过强制类型转换,把这段内存映射为 TCP 报头结构体,依次填充源端口、序号、标志位等各个字段;内存空间的后一部分用来拷贝应用层交付的有效载荷数据,最终组装成完整的 TCP 报文段。
补充区分: UDP 报头长度固定为 8 字节,内存大小预先确定;而 TCP 报头是变长结构,最小 20 字节,最大 60 字节,申请内存时需要根据是否携带选项字段,计算好 TCP 报头的总长度,再分配内存。
(4)序号与确认序号
A. 理解可靠性
a. 造成不可靠的原因
现代计算机基本都基于冯・诺依曼体系结构。在同一台主机内部,CPU、内存、外设都属于独立硬件,硬件之间依靠总线完成数据交互:内存与外设之间通过 IO 总线相连,内存与 CPU 之间依靠系统总线通信。同一台机器内部总线的物理距离很短,数据传输出错的概率很低。
但如果通信双方是两台相隔很远的主机,数据需要经过长距离的网络链路传输。物理传输介质变长后,信号衰减、干扰等问题出现的概率提升,数据传输过程出错、丢失的可能性大幅增加。网络传输不可靠的根本来源,就是远距离传输带来的物理链路不确定性。
b. 不可靠问题场景
- 丢包:报文在网络转发途中,因为路由器缓冲区满、链路中断等原因,报文直接被丢弃,接收方无法收到。
- 乱序:同一个发送方发出的多个报文,经过网络时选择了不同路由,后发送的报文先抵达接收端。
- 校验错误(比特位翻转):传输过程受电磁干扰,报文中部分二进制比特发生翻转,导致接收端校验失败。
- 重复:网络延迟引发报文重传,接收端收到两份完全相同的报文。
补充:IP 层本身是无连接、不可靠的服务,只会尽力把报文送到目标主机,不会处理上面这些异常情况。TCP 协议在 IP 层之上,依靠序号与确认序号机制,解决这些问题,向上层提供可靠传输服务。

如何保证自己发送的数据,对方确实收到了?
最直接的方式就是:接收方收到数据后,返回一个应答确认。只有当发送方收到了对应的应答,才能确认之前发出的数据已经被对方正确接收。只有这种 "发送 — 确认" 的闭环成立,才能算可靠。
但通信双方之间永远存在一条最新发出的消息—— 最后一条消息的发送方,在发出之后、收到应答之前,无法 100% 确认这条最新消息是否真的到达了对方。所以,网络中不存在绝对 100% 可靠的传输协议,只有相对的可靠性。在局部范围内 —— 只要发出去的每一段数据都能收到对应的应答 ——TCP 就能做到 "已确认的数据绝不丢" 的可靠保证。
TCP 实现可靠传输的核心机制之一就是确认应答机制(ACK):发送方每发送一个报文段,接收方在收到后会返回一个确认报文段,告知发送方哪些数据已经成功接收。只要发送方收到了对应报文的确认应答,就可以保证这一段数据已经被对方正确接收。
因此,TCP 通信过程中,报文段除了承载应用层数据之外,还可能携带确认信息—— 也就是说,确认应答可以单独作为一个确认段发送,也可以 "捎带" 在反向传输的数据段中一起发送。

我们来看上图是双方通信使用串行方式,即只有收到了确认应答才会继续发送数据。这样的效率是非常低的。但实际工作中并不会这样,而是一方同时发送多条数据段,只要保证所有数据段都有应答就可以:
但是我们看这些数据段到达对面的顺序不一定就是发送的顺序。好比,发送了四个数据段,结果只服务端收到了三个确认应答,那客户端是如何知道哪个数据段发送失败了呢?
就得出结论:因为任何一方都会收到报文,报文中会携带序号,根据报文收到的多个序号进行排序,报文也就可以按序到达。
B. 32 位序号
TCP 依靠报头内的 32 位序号字段,完成数据报文与确认应答的匹配,以此解决网络传输乱序、重复等问题。
TCP 会对发送数据流里每一个字节单独分配编号,这个编号就是序号。通过字节编号机制,TCP 可以保证接收端收到的数据能够按序重组。 每一个 ACK 确认报文都会携带确认序号,用来告知发送方:当前已经成功接收完确认序号之前的全部字节,下一次数据可以从该确认序号位置继续发送。
举个例子:需要发送总计 4000 字节的数据,拆分成 4 个 TCP 报文段,每个报文段携带 1000 字节数据。 这 4 个报文段的 32 位序号,填写的是当前报文所携带数据第一个字节的编号,因此序号依次为:1、1001、2001、3001。


看图可以知道:当主机 B 接收到这 4 个 TCP 报文时,就可以利用这 4 个报头中的序号字段进行排序。
C. 32 位确认序号
TCP 报头里的 32 位确认序号,用于向通信对端反馈接收状态:告知对方当前已经完整接收了该序号之前的全部字节数据,后续发送的数据需要从这个确认序号对应的字节位置开始传输。

比如:客户端发出一个序号为 1 的数据段,报文内承载 1000 字节的数据。当服务端完整接收该报文后,会在回复的应答报头中将 32 位确认序号设置为 1001。这个确认序号 1001 包含两层含义:
- 一是通知客户端,编号 1001 之前的全部字节数据已经成功接收;
- 二是通知客户端,后续传输的数据需要从序号 1001 对应的字节开始发送。
依靠序号与确认序号的配合机制,接收方可以告知发送方:确认序号之前所有连续字节均已收到,并指明下一段数据的起始编号。该机制支持选择性应答,对于丢失的报文可以不返回确认,不用对每一个报文单独应答。
举例:发送的数据都是 1000 字节大小

假设序号为 1001 的数据段在传输途中丢失,没能送达主机 B,但后续序号更大的数据段都成功到达。此时主机 B 返回的所有确认序号都会持续填写 1001。这个确认序号的含义为:主机 B 已经完整接收了字节编号 1001 之前的全部数据,而字节 1001 开始的数据还没有收到。
为什么需要两个独立的序号字段?能不能将 32 位序号与 32 位确认序号合并成一个字段,发送报文时填序号,回复报文时填确认序号?
如果通信场景只是单向传输,一方只发数据、另一方只负责接收应答,那么合并字段的思路看似可行。但 TCP 属于全双工通信协议,通信双方可以同时向对方发送数据。在双向同时传输的场景下,确认应答可以直接捎带在本方要发送的数据报文中,不需要单独发送应答报文。
这就意味着双方发出的每一个 TCP 报文段,都具备双重作用:一方面需要填写 32 位序号,标记本次发送数据字节的起始编号;另一方面还需要填写 32 位确认序号,用来确认对方此前发来的数据,告知对方后续数据的起始发送位置。因此序号和确认序号必须相互独立,不能压缩合并为同一个字段。
(5)16 位窗口大小
TCP 协议内部维护着发送缓冲区与接收缓冲区。
- 当应用层调用 write 或者 send 函数发送数据时,数据并不会直接发送到网络上,而是先从应用层拷贝到 TCP 的发送缓冲区;
- 同理,当应用层调用 read 或者 recv 读取数据时,只是把 TCP 接收缓冲区里的数据拷贝到应用程序内存。
从底层本质来看,TCP 通信的过程,本质是通信双方缓冲区之间的数据交互:发送方依靠自身发送缓冲区暂存待发数据,接收方依靠接收缓冲区存放收到的数据。双方各自同时拥有发送缓冲区和接收缓冲区,数据在两组缓冲区之间完成拷贝流转。
这样的缓冲区机制会带来两类问题:
客户端发送数据的速率既不能过快,也不能过慢。那 TCP 如何对发送速率进行管控?
TCP 需要实现传输速率的控制,发送方必须实时了解对端接收缓冲区的剩余容量,接收方会持续向发送方同步自身的接收能力,也就是接收缓冲区剩余空间大小,这套机制就叫做流量控制。
接收方的接收能力由什么决定?
由接收方接收缓冲区剩余空间大小决定。
发送方如何获取对端缓冲区剩余空间?
依靠 TCP 报头中的 16 位窗口大小字段。接收方在回复的报文中填写该字段,告知发送方当前本机接收缓冲区剩余容量,也就是当下的数据接收能力。发送方读取窗口大小的值,以此动态调整自己的数据发送速度。
- 窗口大小数值越大,代表接收端剩余缓冲区空间充足,接收能力强,发送端可以提高数据发送速率;
- 窗口大小数值越小,代表接收端剩余缓冲区空间不足,接收能力弱,发送端需要降低发送速率;
- 如果窗口大小为 0,代表接收端的接收缓冲区已经存满,此时发送方应当暂停发送新的数据。
补充:窗口大小字段固定为 16 比特,因此理论最大窗口大小为 64KB。当传输大量数据、需要更大窗口时,可以借助 TCP 报头选项字段中的窗口扩大选项,突破 64KB 的限制。
(6)六个标志位

为什么需要多个标志位?
TCP 报文存在多种类型,包括传输业务数据的普通报文、建立连接报文、断开连接报文、单纯的确认报文等等。不同类型报文需要触发不同处理逻辑:收到普通数据报文,就将数据存入接收缓冲区;收到建连报文,则执行三次握手流程。TCP 设置六个标志位,就是用来区分报文类型,告知接收端该如何处理当前报文。
A. SYN
当报文 SYN 标志置 1,代表这是连接建立请求报文。SYN 仅在建立连接阶段置位,双方进入正常数据传输阶段后,SYN 不再启用。
B. FIN
当报文 FIN 标志置 1,代表这是断开连接请求报文。FIN 只在释放连接阶段置位,正常通信过程中不会设置 FIN 标志。
C. ACK
ACK 为确认应答标志。只要报文携带确认信息,ACK 就置 1,绝大多数 TCP 报文的 ACK 都是 1,代表该报文可以完成对已接收数据的确认。 除了第一次握手的连接请求报文之外,后续几乎所有报文都会开启 ACK。第一次客户端发起的连接请求报文,此前双方没有任何数据交互,不存在可以确认的数据,因此 ACK 置 0。 TCP 支持捎带确认机制,双方传输业务数据时,可以顺带在当前报文里携带对端数据的确认应答,不需要单独发送确认报文。
D. RST
RST 即复位标志。报文 RST 置 1 时,通知对端终止当前连接,重新建立新连接。 场景一:通信双方尚未成功建立连接,一方直接发送业务数据,接收方会回复 RST 置 1 的报文,要求对方重新发起连接。 场景二:服务端网络异常断开(例如网线拔出),但客户端并未感知,依旧持续发送数据,此时服务端返回 RST 报文,强制客户端重置连接状态。
E. PSH
PSH 为推送标志。报文 PSH 置 1,用于通知接收方上层应用尽快读取接收缓冲区的数据。 如果接收缓冲区剩余空间不足,发送方需要暂停发送,等待上层程序读取缓冲区释放空间。此时发送方可以设置 PSH 标志,催促接收方应用读取缓冲区数据。
F. URG & 16 位紧急指针
URG 是紧急标志。报文 URG 置 1,代表报文中包含紧急数据,需要优先处理。 TCP 默认保证数据按序交付,这是它的可靠特性,但紧急数据可以跳过排队,优先处理。 紧急数据不一定是整个报文载荷,仅其中一小部分。TCP 报头搭配 16 位紧急指针字段,存放偏移量,用来定位紧急数据的末尾位置。紧急指针仅有一个,只能标记一处位置。 URG 用于传输 TCP 带外数据,带外数据不走常规数据流队列,接收方会优先处理。举个例子:已经发送大量数据,对方还在处理,但此时需要终止本次传输,就可以发送 URG 标记的带外数据,关闭套接字。






