
文章目录一、从应用消息到网络帧先区分“单位”和“处理操作”1. 分层协议的数据单位不意味着每层都把许多上层单位合成一个本层单位2. “应用层单位就是字节”为什么不准确3. 为什么 TCP 需要组织报文段二、IPv4 数据报与首部完整结构和字段定位1. 为什么 IPv4 需要单独的首部2. 每个字段的位宽3. Version字段宽度、编码与格式选择4. IHL为什么首部最短 20 字节、最长 60 字节5. Total Length为什么不能与 IHL、TCP 数据长度或以太网帧长度混淆6. 用一个完整封装案例推导长度7. Protocol先确定“里面是什么协议”再由该协议找相应接收端点8. 源 IP 与目的 IP都是 32 位网络层地址9. Identification、Flags、Fragment Offset先理解结构与用途10. TTL限制持续转发不表示 TCP 连接的存活时间11. Header Checksum只校验 IPv4 首部算法不是 CRC三、DSCP 和 ECNIPv4 的服务分类与拥塞标记1. 原来的 TOS 字节现在怎么理解2. 中间设备标 CE 与发送端收到反馈不是同一个动作四、长度约束怎样跨层协作TCP MSS、路径 MTU 与 IPv4 分片1. 200000 字节不是一个巨型 IPv4 数据报2. MSS 是 TCP 数据长度限制不是 IP 数据报长度3. IPv4 的 65535 与链路 MTU 的 1500是不同约束4. 路径 PMTU 的真实值与发送端已知值必须区分5. TCP 分段与 IPv4 分片的根本区别五、IPv4 分片首部和重组本轮实际解释过的机制边界1. 分片不会把原始 IPv4 首部切成几小块2. IPv4 重组为何要用 Identification 与 Fragment Offset六、两种顺序系统TCP 的字节序列号与 IPv4 的分片偏移1. TCP Sequence Number 不是“每个 TCP 包一个连续整数编号”2. IPv4 Fragment Offset 不是 TCP Sequence Number3. 分片乱序与 TCP 报文段乱序可以在同一次通信中同时发生七、进程、JVM、Socket 与 TCP 端口对象关系不能混在一起1. 进程、线程、Java Socket 不是同一种对象2. 新创建的 Socket 不一定立即拥有显式指定的本地端口3. TCP 四元组为什么需要四项4. 监听 Socket 与已建立连接 Socket 的职责不同5. 两个进程能不能监听同一 TCP 端口不要拿已连接四元组代替监听绑定规则八、IP 交 TCP 的不只有数据字节跨层元数据与准确解复用1. TCP 首部只有端口号TCP 怎样取得源 IP、目的 IP2. IP 源地址不是最后一跳的网卡目的地址也不必一一对应实际入站接口九、输出接口、源 MAC、目的 MAC路由与 ARP 分别负责什么十、完整端到端过程Java Socket 写入到另一端 read十一、容易互相混淆的机制按“谁处理什么”逐项对照一、从应用消息到网络帧先区分“单位”和“处理操作”1. 分层协议的数据单位不意味着每层都把许多上层单位合成一个本层单位网络通信中常见的数据单位包括应用协议消息、TCP 报文段segment、IPv4 数据报datagram以及以太网帧frame。它们分别属于不同协议的语义层次应用协议定义消息的结构和边界TCP 为应用提供双向、有序的字节流并在传输时形成报文段IPv4 用数据报封装相应载荷以进行网络层传递以太网再用帧完成当前链路上的发送。应用程序定义一条消息 ↓ 编码为字节 TCP 管理字节流组织一个或多个 TCP 报文段 ↓ 典型 TCP/IPv4 封装 IPv4 首部 一个完整 TCP 报文段 原始 IPv4 数据报 ↓ 以太网封装 以太网首部 IPv4 数据报 FCS 以太网帧典型情形各层定义本层数据单元不代表各层必须执行固定的“多个上层包合成一个本层包”操作。正常未分片 TCP/IPv4 封装中一份 IPv4 数据报的载荷通常是一份 TCP 报文段。IPv4 分片则可能让一个原始数据报变为多个分片这种操作并不等于把 TCP 报文段重新划分成几份独立的 TCP 报文段。隧道、网卡卸载等特殊机制可以有不同的内部表示不能因此修改普通线上协议封装模型。2. “应用层单位就是字节”为什么不准确应用程序确实会将待传输的内容编码为字节但应用层协议往往有自己的消息单位例如一份登录请求、一次查询及其字段结构。一份应用消息可以被编码成很多字节多份消息也可依次写入同一 TCP 连接。TCP 所提供的服务不保存应用消息边界。程序对同一 Socket 连续执行两次write()接收端可能通过一次read()读到两次写入的字节也可能通过几次read()才读取完一次写入的数据。反过来一个 TCP 报文段也未必恰好等于一条完整的应用层消息。应用协议如需识别边界应自行约定长度、分隔符或其他消息格式。3. 为什么 TCP 需要组织报文段TCP 管理的是连续的字节流但网络不能直接发送“一条无限长的逻辑字节流”。TCP 需要在发送时选择适当的字节范围附加源/目的端口、序列号等 TCP 首部信息形成报文段再由 IP 承载。发送端还必须考虑可发送数据量、接收端窗口、拥塞状态、MSS、适用的路径长度限制与实际待发送字节数。这些机制服务于不同判断流量控制主要限制接收端允许发送的未完成接收数据量拥塞控制限制网络当前适合保持的在途数据量MSS 与路径 MTU 等影响单个 TCP 报文段的数据长度。能继续发送一万字节不等于应形成一个携带一万字节数据的 TCP 报文段单段最多可以承载 1460 字节也不意味着每个报文段都必须装满。二、IPv4 数据报与首部完整结构和字段定位1. 为什么 IPv4 需要单独的首部TCP 首部中的端口、序列号等字段不承担整个网络层的寻址和转发职责。IPv4 为了让发送主机、路由器和接收主机依据共同格式处理数据报需要自己的首部指明格式版本、首部/总长度、服务及拥塞信息、分片相关状态、TTL、上层协议类型、首部校验和、源/目的 IPv4 地址并可携带选项。因此承载 TCP 的普通 IPv4 数据报是┌─────────────────────────────────────────────────────────┐ │ IPv4 首部 │ ├─────────────────────────────────────────────────────────┤ │ IPv4 载荷TCP 首部 TCP 数据 │ └─────────────────────────────────────────────────────────┘从 IPv4 视角整个 TCP 报文段是其载荷从 TCP 视角同一段字节又可分为 TCP 首部和 TCP 数据。不要把 IPv4 载荷直接等同于应用层净数据。2. 每个字段的位宽一个字节等于 8 bit。4 位无符号字段可表示 0158 位可表示 025516 位可表示 065535。但字段可编码多少个数字与协议允许哪些值是两件事例如 IHL 小于 5 在标准 IPv4 首部中是不合法的。IPv4 首部按每行 32 bit 的格式展示0 31 ┌───────┬───────┬──────────────┬──────────────────────┐ │Version│ IHL │ DSCP ECN │ Total Length │ │ 4 bit │ 4 bit │ 8 bit │ 16 bit │ ├───────┴───────┴──────┬───────┴───┬──────────────────┤ │ Identification 16 bit│Flags 3 bit│Fragment Offset 13│ ├──────────────────────┼───────────┴──────────────────┤ │ TTL 8 bit│Protocol 8│ Header Checksum 16 bit │ ├──────────┴───────────┴──────────────────────────────┤ │ Source IPv4 Address 32 bit │ ├──────────────────────────────────────────────────────┤ │ Destination IPv4 Address 32 bit │ ├──────────────────────────────────────────────────────┤ │ Options Padding如果存在长度可变 │ └──────────────────────────────────────────────────────┘ 随后才是 IPv4 载荷。上述前五行共5 × 32 bit 160 bit 20 字节构成固定首部。真实首部在其后可以包含可选项及必要填充所以首部长度是可变的。字段宽度本阶段已讲解的作用Version4 bit声明 IPv4 首部格式版本IPv4 为二进制0100IHL4 bit以 4 字节为单位记录 IPv4 首部长度DSCP ECN8 bit差分服务分类和显式拥塞通知Total Length16 bit整个 IPv4 数据报的字节数含首部和载荷Identification16 bit用于分片身份识别的相关信息Flags3 bit保留位、DF 与 MF 等分片控制Fragment Offset13 bit分片载荷在原始 IPv4 载荷中的偏移单位为 8 字节TTL8 bit限制数据报被持续转发Protocol8 bit指示载荷协议例如 TCP、UDP、ICMPHeader Checksum16 bit对 IPv4首部进行差错检测Source Address32 bit当前 IPv4 数据报中的源地址Destination Address32 bit当前 IPv4 数据报中的目的地址Options、Padding可变可选控制信息和对齐填充3. Version字段宽度、编码与格式选择IPv4 的 Version 占 4 bit值为二进制0100即十进制 4。字段宽度是 4 位不表示“版本号必须等于字段位数”这四位记录一个代表 IP 格式的编码。IPv6 的版本值为 6但将 IPv4 首部开头四位直接改成 6 并不能得到合法的 IPv6 数据报因为后续首部布局及处理规则也不同。IPv6 的完整结构属于后续单独阶段。4. IHL为什么首部最短 20 字节、最长 60 字节IHL 是 Internet Header Length占 4 bit其单位不是 1 字节而是32 bit 4 字节。IPv4 首部长度字节IHL×4固定字段共 20 字节所以最小有效 IHL 为 54 位最大无符号值是 15所以最大首部长度是 60 字节。合法长度是 20、24、28、……、60 字节。没有 Options 时通常 IHL5首部 20 字节。假设 IHL7首部为7×428字节其中在 20 字节固定区之外可有 8 字节选项与填充空间。以数据报第一个字节的偏移量为 0则 IPv4 载荷起始偏移量是 28也就是第 29 个字节。接收设备若错按 20 字节解析会把部分首部选项误当成载荷。Options 与 Padding可选项携带特定额外控制信息例如 IPv4 曾定义记录路由、时间戳等选项如果选项区尚未达到 4 字节对齐要求需要填充。假设固定首部 20 字节、选项 6 字节、填充 2 字节总首部 28 字节IHL7。最多 40 字节是 Options 与填充共同使用的剩余首部空间并不是首部可以在 60 字节之外再追加填充。5. Total Length为什么不能与 IHL、TCP 数据长度或以太网帧长度混淆Total Length 占 16 bit以字节为单位表示本次 IPv4 数据报首部与载荷的总和。Total LengthIHL×4IPv4 载荷字节数因此也可以由两个字段求出 IPv4 载荷IPv4 载荷字节数Total Length−4×IHL例如 IHL6、Total Length1200首部 24 字节载荷1200−241176字节以 0 为起始偏移量首部占 023载荷占 241199。Total Length 最大可编码2^16−165535字节这是普通 IPv4 数据报的整体上限若首部 20 字节理论上的最大 IPv4 载荷为 65515 字节不是 65535 字节。它同样不会包含外层以太网首部、FCS、物理层前导码等开销。例如普通无 VLAN 的 Ethernet II 以太网帧携带恰好 1500 字节的 IPv4 数据报帧首部 14 字节、IPv4 数据报 1500 字节、FCS 4 字节则从目的 MAC 字段到 FCS 为14150041518字节IPv4 Total Length 仍为 1500而非 1518。与物理层实际传送开销不能混算。6. 用一个完整封装案例推导长度假设源地址192.0.2.10、目的地址198.51.100.20IPv4 的 Version4、IHL5、Protocol6、Total Length1500TCP 不携带额外选项且 TCP 首部为 20 字节。IPv4 数据报总长1500 字节 ├── IPv4 首部20 字节 ← IHL5 └── IPv4 载荷1480 字节 ← 1500−20 ├── TCP 首部20 字节 └── TCP 数据1460 字节 ← 1480−20这不代表 Java 程序一次write()必须恰好写 1460 字节也不代表网上一定已经发送了 1500 字节应用业务数据。这里 1460 仅是在给定长度与首部条件下计算出来的 TCP 数据量。7. Protocol先确定“里面是什么协议”再由该协议找相应接收端点IPv4 Protocol 占 8 bit常见值是1ICMP、6TCP、17UDP。收到某份 IPv4 数据报后IP 层先根据本地接收等规则决定能否交付再根据 Protocol 把相应载荷交给合适的上层协议处理模块。区分三种“告诉下一层是什么”的信息以太网 EtherType 0x0800 → 以太网载荷采用 IPv4 IPv4 Protocol 6 → IPv4 载荷是 TCP TCP 目的端口 8080 → 供 TCP 端点/连接处理使用EtherType、Protocol、TCP 端口存在于不同层的首部中作用不同。IP 层不依据 TCP 目的端口直接挑选 Java 进程。8. 源 IP 与目的 IP都是 32 位网络层地址IPv4 地址通常写成四个以点分隔的十进制数例如192.0.2.10实际上是四组 8 bit 的组合11000000 00000000 00000010 00001010。它在 IPv4 首部中存放的是 32 位地址值不是字符串字符本身。普通不改写地址的 IP 转发中源、目的 IPv4 地址通常保持其端到端网络层意义目的 IP 参与转发和本地接收判定源 IP 表示报文头中的源地址。但 NAT 等机制可能修改相关地址源/目的 IP 不能直接等同于应用进程、TCP 端口或一台物理设备永久不变的身份。地址的前缀、掩码、CIDR 与完整路由匹配仍属于后续阶段。9. Identification、Flags、Fragment Offset先理解结构与用途分片相关字段包括 Identification16 bit、Flags3 bit保留位、DF、MF和 Fragment Offset13 bit。Identification 是 IPv4 分片重组识别的相关信息不是全互联网永久唯一编号DF 与是否允许相关分片有关MF 表示后面是否还有分片通常最后一个分片 MF0Fragment Offset 用 8 字节单位表示分片载荷在原始 IPv4 载荷中的位置。分片载荷偏移字节8×Fragment Offset本阶段讲到了字段位置、用途、对象与简化分片示意没有完成ID/DF/MF 的全部特殊规则、片长/对齐的逐数字演算、重组异常及丢片超时的正式系统教学。10. TTL限制持续转发不表示 TCP 连接的存活时间TTLTime To Live占 8 bit通常在 IPv4 路由器转发数据报时被递减用于限制数据报无限持续转发尤其避免路由环路使其一直在网络中循环。实际 IPv4 语境下它经常可以作为转发跳数限制来理解。TTL 不是毫秒数、不是应用数据保留时间也不是 TCP 连接寿命各主机初始 TTL 不必相同。TTL 耗尽、ICMP Time Exceeded、诊断工具的具体机制留待后续第四阶段完整推导。11. Header Checksum只校验 IPv4 首部算法不是 CRCHeader Checksum 占 16 bit基于 IPv4 首部按 16 位数据单元计算的反码求和校验机制用于检测IPv4 首部的某些传输错误不覆盖整个 TCP 数据部分。由于转发中 TTL 等首部字段可能改变IPv4 首部校验和也要相应更新可重算或按等效方式增量调整。校验通过不代表应用数据必然完整、可靠到达也不意味着全部错误都能检测出来。完整反码求和的逐步二进制演算留在第四阶段。三、DSCP 和 ECNIPv4 的服务分类与拥塞标记1. 原来的 TOS 字节现在怎么理解IPv4 首部中一个 8 位字段历史上称 Type of ServiceTOS。现代解释常将该字节区分为6 位 DSCP 2 位 ECN不应把历史旧式优先权和服务位的划分原样当成今天一切设备通行的处理模型。一个 8 bit 字节 ┌────────────────────────┬────────────┐ │ DSCP6 bit │ ECN2 bit │ └────────────────────────┴────────────┘DSCPDifferentiated Services Code Point为网络设备选择相应差分服务处理提供分类信息例如在支持 QoS 的环境中影响队列调度、丢弃策略等。但 DSCP 并不自动保证最低延迟、某个确定带宽或“永不丢包”网络设备要有相应配置和策略且可重标记 DSCP。目的 IPv4 地址和路由信息负责相应的转发判断DSCP 与逐跳服务处理相联系两者不可简单替代。ECNExplicit Congestion Notification用于显式拥塞通知2 位常见值是ECN 位含义00Not-ECT未声明相应 ECN 能力10ECT(0)ECN-capable 标记之一01ECT(1)ECN-capable 另一标记11CECongestion Experienced经历拥塞标记2. 中间设备标 CE 与发送端收到反馈不是同一个动作假设 A 向 B 发送已经声明 ECN 能力的 IPv4 数据报经过中间路由器 R。R 的队列管理策略在符合相应条件时可以将这份数据报的 ECN 字段改为 CE然后让它继续沿原方向向 B 前进。发送方 A 路由器 R 接收方 B │ │ │ ├──── IP 数据报 ECT ────►│ │ │ ├──── 原数据报标 CE ─────────────►│ │ │ │ │◄────────────── TCP 的相应拥塞反馈反方向 ─────────────┤ │ │ 发送端 TCP 依协议规则对拥塞信号作出响应关键不是“R 自动另外创建一个发送给 A 的拥塞告警包”。典型经典 TCP ECN 中B 接收到带 CE 的数据报后IP/TCP 协议栈配合通过 TCP 相应确认/反馈机制向发送方反映拥塞例如 ECE 信号发送端再按协议规则调整拥塞控制行为。发送端不需要直接知道是哪台具体路由器打了 CE单凭 CE 也通常不能定位拥塞设备。网络设备也不能不论条件都把 Not-ECT 数据报改为 CE即使支持 ECN数据报也仍可能被丢弃。CE 标记不是“已经丢掉了包还在这份包里标记”也不意味着可靠送达或免除 TCP 对丢包的处理。完整 TCP ECN 协商、ECE/CWR 状态交互及主动队列管理算法超出本轮已经正式讲清的范围。四、长度约束怎样跨层协作TCP MSS、路径 MTU 与 IPv4 分片1. 200000 字节不是一个巨型 IPv4 数据报应用程序可以向一个 TCP 连接提供总量 200000 字节的待发送数据。这是应用数据总量不是单个 TCP 报文段、单个 IPv4 数据报的长度。TCP 根据发送状态把字节流组织成若干 TCP 报文段普通 IPv4 将各自报文段承载于相应数据报中。假设为演示而设定以太网路径有效 MTU 为 1500 字节、IPv4 首部 20 字节、TCP 首部 20 字节、没有其他额外首部开销、发送端满足其余发送条件并尽量按 1460 字节数据形成满载段。于是1500−20−201460 字节 TCP 数据200000136×14601440可以形成 136 个载荷 1460 字节的报文段再加一个载荷 1440 字节的报文段共 137 个。但这只是指定条件下的组织示例。TCP 不被要求每次都装满、也未必一次性把全部 137 个发送出去网络上的段数量还可能因实现、选项、发送条件等不同而变化。一次write(200000 bytes)也不对应一次 TCP 段或一次 IP 数据报。TCP 允许连接累计传输远超过 IPv4 单报最大长度的字节流不需要首先造一个超出 65535 字节的普通 IPv4 数据报再由 IP 去分片。2. MSS 是 TCP 数据长度限制不是 IP 数据报长度MSSMaximum Segment Size是 TCP 报文段中TCP 数据部分的最大长度相关参数不含 TCP 首部双方可以在连接建立时分别使用 MSS 选项声明自身愿意接收的最大 TCP 数据段大小。A 向 B 发数据需要考虑 B 的接收 MSS 声明B 向 A 发数据则考虑 A 的声明因此两个方向可以不同。发送端实际组段还要考虑路径 MTU、IPv4/TCP 首部及其他开销、当前发送窗口、拥塞窗口和待发送数据。假设 B 声明接收 MSS1460但 A 当前面临 IPv4 PMTU1280 且两个首部均为 20 字节那么不经分片的典型 TCP 数据长度上限为1280−20−201240字节而不是硬按 1460 字节发出。即使上限为 1240一次实际发送仍可能只有几百字节。3. IPv4 的 65535 与链路 MTU 的 1500是不同约束IPv4 Total Length 的 65535 字节是该数据报格式能表示的整体长度上限。某条链路的 MTU 则限制通过该链路时一个网络层数据报能以多大尺寸直接承载。一个 3000 字节的 IPv4 数据报可以符合 IPv4 自己的格式上限却不能作为一个完整数据报直接通过 MTU 1500 的普通输出链路在条件允许时可分片不允许分片时需要采取其他处理例如丢弃并在适用时发出 ICMP 错误反馈。所以“IP 层能否在内部容纳这份数据报”和“下一段链路可否不分片直接发出”不是一个问题。TCP 尽量形成适合路径的报文段既是利用下层网络能力也能减少 IPv4 分片需求TCP 并没有因此代替 IPv4 进行分片。4. 路径 PMTU 的真实值与发送端已知值必须区分路径 MTUPMTU指特定 IP 路径可不经分片通过的最大 IP 数据报尺寸其真实约束受沿途链路等条件限制。假设路径三段 MTU 分别为 1500、1400、1280且无其他特殊封装则该路径的实际 PMTU 为最小值 1280。如果发送端准确知道当前未变化的这条路径PMTU 为 1280并遵守它发包则不会又在该路径上遇到比 1280 更小的下一跳链路否则“已经准确知道”与“路径上还有更小者”相矛盾。现实中可能出现发送端只知道过时或估计的 PMTU路由改变、某段新增封装开销、探测信息不全或未及时更新都可能让发送端认为可用 1500实际路径却只能不分片承载 1280。“可能遇到更小 MTU”必须带上“当前已知值与真实路径条件不一致或路径发生变化”等前提。完整 PMTUD 如何发现并修正数值是第三阶段的正式教学任务。5. TCP 分段与 IPv4 分片的根本区别TCP 分段以某个方向的 TCP 字节流为组织对象创建具有各自 TCP 首部的 TCP 报文段报文段的序列号标识自己所承载字节在 TCP 字节流中的起始位置。TCP 不需要按应用消息边界分段。IPv4 分片以一份已经存在的 IPv4 数据报及其 IPv4 载荷为处理对象按允许的分片规则形成多个带各自 IPv4 首部的 IP 分片。它不按 TCP 消息或 TCP 序列号重新创建多条 TCP 报文段原来的载荷可能从 TCP 首部开始后面的分片只含原 TCP 数据的后续字节。因而 IPv4 分片并不是“先在 IP 层装入多份独立的 TCP 报文段装不下了再拆”。在典型 TCP/IPv4 模型中IPv4 分片的来源是一个原始 IPv4 数据报而其载荷可恰好是一份完整 TCP 报文段。五、IPv4 分片首部和重组本轮实际解释过的机制边界1. 分片不会把原始 IPv4 首部切成几小块假设原始 IPv4 数据报 ┌────────────────────────┐ │ 原始 IPv4 首部 │ ├────────────────────────┤ │ TCP 首部 │ │ TCP 数据全部字节 │ └────────────────────────┘在符合条件时发生 IPv4 分片可以得到IPv4 分片 1 ┌────────────────────────┐ │ 分片 1 自己的 IPv4 首部│ ├────────────────────────┤ │ TCP 首部 部分 TCP 数据│ └────────────────────────┘ IPv4 分片 2 ┌────────────────────────┐ │ 分片 2 自己的 IPv4 首部│ ├────────────────────────┤ │ 另一部分 TCP 数据 │ └────────────────────────┘ IPv4 分片 3 ┌────────────────────────┐ │ 分片 3 自己的 IPv4 首部│ ├────────────────────────┤ │ 余下的 TCP 数据 │ └────────────────────────┘每个 IP 分片本身都需要足够完整的 IPv4 首部供相应网络层处理不是原始 IPv4 首部被切碎成几份残缺的“小首部”。原数据报无选项且首部为 20 字节时各分片自己的 IPv4 首部也可以是 20 字节变小的是各分片的数据报总长度。不同分片的 Total Length、Offset、MF、首部校验和等字段可能不同某些 Options 的复制规则也是特别条件不能认定各分片首部逐字节完全一样。2. IPv4 重组为何要用 Identification 与 Fragment Offset分片可能乱序到达。例如同一原始 IPv4 数据报的载荷被分为 A、B、C 三部分网络实际先收到 B、再收到 A、最后 C。接收端不能按分片到达顺序拼接否则载荷字节位置错误。IPv4 的相关标识信息帮助分辨属于同一原始数据报的分片Fragment Offset 表明当前分片载荷对应原始载荷的起始位置MF 等信息参与完整性判断。重组的逻辑目标是恢复原始 IPv4 载荷的正确字节布局并构造相应的完整网络层数据报表示。不会把IPv4 首部1 载荷A IPv4首部2 载荷B ……简单首尾拼接也不要求完整复原发送时首部全部原始比特例如 TTL 在网络中可以合法变化。对重组结果还需处理相应总长度、分片状态和首部校验等信息。通常在目的主机的 IP 层有效完成重组后才能按照 Protocol 将完整载荷交给 TCP。如果某必要分片始终缺失就可能无法恢复该原始 IP 数据报TCP 后续可能通过自己的确认/重传机制应对数据缺失却不是直接替代 IP 重组缺失的分片。此处只是分片对象、各片首部、重组对象及顺序的概念和实例。分片字节数/偏移 8 字节对齐逐步演算、Identification/DF/MF 全部判定、特殊 Options、分片丢失和超时及 PMTUD 的完整条件仍归第三阶段正式教学。六、两种顺序系统TCP 的字节序列号与 IPv4 的分片偏移1. TCP Sequence Number 不是“每个 TCP 包一个连续整数编号”TCP 为一条 TCP 连接中某一个传送方向的字节流管理序列号空间。对于携带普通数据的 TCP 报文段其首部 Sequence Number 表示这段数据第一个字节的序列号而不是简单地给每个报文段编 1、2、3。假设忽略 SYN 等控制位占用序列号空间的细节一个方向准备发送 3000 字节起始数据字节序号为 1000且为了演示分成三段TCP 报文段TCP 首部 SEQ数据字节序号范围A100010001999B200020002999C300030003999假设 C、A、B 的到达顺序不同于发送顺序TCP 可依据序列号、当前接收状态及缓冲信息识别乱序数据并在满足接收条件时向应用程序提供按序、无重复的字节流。它管理的是字节范围不把应用一次write()、一个 TCP 段或者不同 TCP 连接统统纳入同一个全局编号。2. IPv4 Fragment Offset 不是 TCP Sequence NumberIPv4 分片偏移描述的是一份原始 IPv4 数据报内部的 IPv4 载荷位置而 TCP SEQ 描述的是一条 TCP 连接一个发送方向的字节流的位置。一个 TCP 报文段可能被某份原始 IPv4 数据报承载如果这份 IPv4 数据报分片后续非首片可能完全没有 TCP 首部更没有独立完整的 TCP 报文段起始序列号。IPv4 也可以承载 UDP/ICMP 等没有 TCP 字节流 SEQ 机制的协议因此 IP 分片重组不能依赖 TCP。3. 分片乱序与 TCP 报文段乱序可以在同一次通信中同时发生仍假设 TCP 报文段 A、B、C 的 SEQ 分别是 1000、2000、3000B 所在的 IPv4 数据报在路径中分为 B1、B2。到达顺序可能是接收顺序 1完整 IPv4 数据报 C 接收顺序 2IPv4 分片 B2 接收顺序 3完整 IPv4 数据报 A 接收顺序 4IPv4 分片 B1C 未分片IP 可把完整 TCP 段 C 交给 TCP。TCP 看出数据在 30003999前面尚有缺口可以保留乱序数据等待。B2 是分片尚没有 B1IP 层按分片机制等待不应将 B2 当成独立 TCP 报文段直接交 TCP。A 到来IP 交给 TCPTCP 得到 10001999 的数据。B1 到来在假定分片合法且全到齐时IP 层按照原始 IPv4 载荷偏移恢复 B 的完整载荷交给 TCPTCP 识别到 B 的 SEQ2000。缺失的 20002999 字节区间得到补齐后TCP 能按其规则使 10003999 的相应连续字节范围依次可供应用读取。IPv4 的任务B2 B1 → 按 IP 片偏移恢复一个完整 TCP 报文段 B TCP 的任务 C、A、B → 按 TCP 字节序号恢复有序字节流 A、B、C所以IPv4 首先在单个原始 IP 数据报范围内恢复载荷TCP 随后在连接字节流范围内处理不同报文段的乱序、重复、缺失与按序交付。两种位置机制的对象和层级不同不能只保留其中一种。若 B2 永远丢失IP 可能无法重组 BTCP 必须通过自己后续的可靠性处理机制应对缺失的 TCP 字节范围。七、进程、JVM、Socket 与 TCP 端口对象关系不能混在一起1. 进程、线程、Java Socket 不是同一种对象操作系统管理进程通常一次java Main运行会启动一个 JVM 进程其中有多个 Java 线程的可能。一个 JVM 进程可以创建并使用多个 Java Socket 对象各 Socket 可关联不同的操作系统套接字资源或 TCP 通信端点。Socket 不是线程它不会因为数量与线程相同才可以通信多个线程可能访问一个 Socket一个线程也能处理多个 Socket。JavaSocket是供 Java 程序使用网络操作的对象内核中的套接字及 TCP 连接状态是 OS 管理的实现资源。TCP 端口则是 TCP 首部中的 16 位数值字段不是额外存在于 Socket 与 TCP 之间、字节流必须穿过的一台“端口设备”。2. 新创建的 Socket 不一定立即拥有显式指定的本地端口客户端可以使用类似import java.net.Socket; try (Socket socket new Socket(198.51.100.20, 8080)) { // 使用 socket.getInputStream()/getOutputStream() }这里8080表示远端目的 TCP 端口并不要求客户端自己也使用本地 8080。连接时系统通常可以为客户端自动选择适当的本地 IP 和临时端口例如本地192.0.2.10:50000。刚创建、还没有绑定或连接的 Socket未必已经有确定的本地端口因此“一创建 Socket 就手动绑定一个唯一端口”不是 TCP Socket 的普遍规则。端口号与进程不是固定一一对应。一个进程可以使用多个 Socket、多个本地端口许多已经建立的 TCP 连接也可以共享一个服务器本地端口。3. TCP 四元组为什么需要四项典型一条已建立的 TCP/IPv4 连接可由两个端点信息标识源 IP源 TCP 端口目的 IP目的 TCP 端口从正在观察 Socket 的主机而言也可表述为(本地 IP本地端口远端 IP远端端口)。注意“源/目的”随当前报文段发送方向改变“本地/远端”则随观察者所在主机改变。例如客户端192.0.2.10:50000与服务器198.51.100.20:8080客户端发送请求时源端点是客户端目的端点是服务器服务器发响应时源端点变成服务器目的端点变成客户端。但在服务器 B 的 Socket 视角本地始终指 B远端始终指 A。仅凭两个 TCP 端口无法区分所有连接。设同一客户端192.0.2.10:50000同时连接服务器本地198.51.100.20:8080与198.51.100.21:8080TCP 源/目的端口相同但服务器目的 IP 不同仍是不同 TCP 端点组合。这也是 TCP 必须从 IP 处理路径获得相关地址信息的原因之一。4. 监听 Socket 与已建立连接 Socket 的职责不同典型 Java 服务器import java.net.ServerSocket; import java.net.Socket; try (ServerSocket server new ServerSocket(8080)) { Socket client1 server.accept(); Socket client2 server.accept(); // 两个 client Socket 可在适当时机用于各自已建立连接的数据收发 }这是说明 API 角色的示意代码accept()可以阻塞等待新的连接此代码不是已执行的实验也不是生产级并发服务器实现。ServerSocket对应监听端点本地可为198.51.100.20:8080。TCP 三次握手的核心状态处理由操作系统协议栈进行Javaaccept()通常取得一个已完成握手、可被应用接受的连接 Socket。它不是返回一个尚未建立的 TCP 连接让 Java 再手动从头执行三次握手。监听 Socket 自身处于监听状态不是与某个确定客户端已经建立的数据连接已连接 Socket 则用于对应连接的双向数据收发。例如监听 Socket本地 198.51.100.20:8080状态 LISTEN 已连接 Socket 1 本地 198.51.100.20:8080 远端 192.0.2.10:50000 已连接 Socket 2 本地 198.51.100.20:8080 远端 192.0.2.11:50001服务器可以同时服务两条不同 TCP 连接却不需要给两个客户端分别增设 8081、8082 监听端口。TCP 协议栈结合完整端点信息和连接状态将收到的报文段交给相应连接及 Socket 接收路径。5. 两个进程能不能监听同一 TCP 端口不要拿已连接四元组代替监听绑定规则已连接 Socket 能否共用某个服务器本地端口与两个监听 Socket 能否独立绑定同一本地 IP端口是不同判断前者可以通过不同远端端点形成不同 TCP 连接后者由本地地址、端口、监听状态、通配地址绑定、OS 及相关 Socket 选项决定。例如同一主机的两个 Java 进程都以通常独占方式监听相同具体本地 IP 和 TCP 8080后尝试者通常遇到地址已占用。两个监听端点若采用不同具体本地地址或操作系统允许并正确配置了特定复用机制如适用的SO_REUSEPORT可能产生不同结果通配地址0.0.0.0也可能影响冲突判断。TCP 与 UDP 有不同传输协议的端口命名空间。不能只说“四元组不同就允许任何两个进程监听同一端口”因为监听端点尚不与固定远端四元组相联系。八、IP 交 TCP 的不只有数据字节跨层元数据与准确解复用1. TCP 首部只有端口号TCP 怎样取得源 IP、目的 IPIPv4 首部包含源/目的 IP、Protocol 等TCP 首部包含源/目的 TCP 端口、序列号等。接收端 IP 层确定本地接收并发现Protocol6后通常将 TCP 载荷交给内核 TCP 处理路径并以协议栈内元数据、缓冲区上下文等方式使 TCP 获得所需 IP 地址信息。IPv4 接收路径已知 源 IP 192.0.2.10 目的 IP 198.51.100.20 Protocol 6 交给 TCP 的处理上下文 完整 TCP 报文段字节 所需 IPv4 相关元数据 TCP 进一步解析 源端口 50000 目的端口 8080 结合完整端点信息 TCP 连接状态 → 定位对应的接收连接不要求复制一整份 IPv4 首部到 TCP 报文段前面也不能因 IP 层已决定本地接收就认为 TCP 无须知道两个 IP 地址。不同本地 IP/相同 TCP 端口可以标识不同的连接。此外TCP/IPv4 校验和计算涉及包含源 IP、目的 IP、协议号、TCP 长度等信息的伪首部伪首部是校验计算的逻辑结构并不是随 TCP 报文段多发送的一份真实 TCP 首部。相关事实说明 TCP 可以依赖 IP 层提供的必要信息但并不负责 IP 层的普通路由转发。2. IP 源地址不是最后一跳的网卡目的地址也不必一一对应实际入站接口假设客户端 A 经 R1、R2 把数据报送至服务器 B无 NAT 情况下报文头源 IP 可以始终是 A 的192.0.2.10但最后一跳实际把帧交给 B 的是 R2 的某个接口。IPv4 源 IP 不是最后一跳路由器的 IP/MAC不单凭它确认数据报从 B 的哪张物理网卡进入也不构成不可伪造的真实设备身份证明。IPv4 目的 IP 参与判定本机是否应当接收及 TCP 本地端点定位但它与物理入站接口也并非在所有网络配置中一一对应一台多网卡主机可能有多个 IP/逻辑接口接收策略因操作系统与设置而异。实际收包接口由网卡驱动和 OS 接收路径上下文可知其信息与 IP 源/目的地址属于不同维度。服务器回复 TCP 数据时TCP 的本地/远端端点决定相关回复端口与地址IP 发送路径仍要依据路由策略决定当前输出接口及下一跳不必因为原请求由某张网卡进入就沿同一张网卡送出。九、输出接口、源 MAC、目的 MAC路由与 ARP 分别负责什么在典型以太网发送中操作系统需要回答三个问题从本机哪个接口出去本帧源 MAC 用哪个本帧目的 MAC 给哪个邻居三者关联但不能全部说成“通过 ARP 查询得到”。假设主机 A 有eth0IPv4192.168.1.10MACMAC-A0 eth1IPv4192.168.2.10MACMAC-A1A 要向远端 B 发送路由与适用的转发规则选定输出接口eth0 下一跳 IPv4 地址192.168.1.1于是普通以太网发送时输出接口依据路由等规则确定为eth0通常不是先由 ARP 决定选 eth0 还是 eth1。源 MAC通常取本地输出接口 eth0 的已配置 MAC即MAC-A0不需要通过 ARP 查询“自己的 IP 对应哪个 MAC”。目的 MAC对应当前链路上要交付的下一跳邻居可以通过 ARP/邻居缓存从192.168.1.1获得例如MAC-R1。因此发送的帧为以太网源 MAC MAC-A0 以太网目的 MAC MAC-R1 其内部 IPv4 数据报源 IP 和最终目的 IP 按相应网络层语义表示下一台路由器转发时一般在其输出链路重新形成相应链路层封装源 MAC 改为本次路由器输出接口的 MAC目的 MAC 则是当前下一跳的 MAC。不能让起始链路的以太网帧首部一路不变地跨越所有路由器。桥接、虚拟网卡、MAC 重写、非以太网链路等场景有不同实现应按环境另行判断。十、完整端到端过程Java Socket 写入到另一端 read假设 TCP 连接已建立客户端 A192.0.2.10:50000 服务器 B198.51.100.20:8080Java 客户端可以执行import java.net.Socket; import java.nio.charset.StandardCharsets; import java.io.OutputStream; try (Socket socket new Socket(198.51.100.20, 8080)) { OutputStream out socket.getOutputStream(); out.write(Hello.getBytes(StandardCharsets.UTF_8)); }在这个示例中Hello对应五个 UTF-8 字节。write()把字节提交给关联 Socket 的 TCP 发送路径返回不等于远端 Java 程序已经读取、处理或持久化该数据。发送端 TCP 按连接状态形成带源端口 50000、目的端口 8080、相应序列号等字段的 TCP 报文段IPv4 再添加源 IP、目的 IP、Protocol6 等字段根据路由和邻居解析决定当前以太网输出接口及 MAC 封装。接收端链路层收到当前链路帧 ↓ IPv4 判断是否本地接收、首部合法与 Protocol6 ↓ 如果该 IPv4 数据报发生分片先按 IP 规则完成有效重组 ↓ TCP 获得载荷 必要源/目的 IP 元数据 ↓ 解析 TCP 源端口50000、目的端口8080等首部 ↓ 查找对应 TCP 连接并根据状态、SEQ、确认等机制处理 ↓ 将当前符合交付条件的有序字节提供给相应 Socket ↓ Java InputStream.read() 读取这些字节服务端示意import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; try (ServerSocket server new ServerSocket(8080); Socket client server.accept()) { InputStream in client.getInputStream(); byte[] buf new byte[1024]; int n in.read(buf); if (n 0) { // 只处理本次实际读到的 n 个字节不假设恰好等于客户端一次 write } }以上只是说明 Socket 与 TCP/IP 收发关系的概念代码未在学习记录中进行实际编译、运行或抓包实验。TCP 可分多次向应用交付字节两次write(Hello)、write(World)可能在读取时呈现一次HelloWorld也可能被分几次读出。应用如果需要每条独立消息就必须自己识别其消息边界。这一链路还说明端口号是 TCP 首部字段和连接状态的识别信息没有Java Socket → 端口硬件 → TCP → IP这样的额外数据传输层。IP 不直接选择 Java 进程TCP/Socket 连接定位及操作系统资源关联机制参与了交付。十一、容易互相混淆的机制按“谁处理什么”逐项对照容易混淆的问题准确关系每个协议层都把多个上层包聚合成一个本层包没有普遍固定数量对应按具体协议定义封装、分段或分片应用层消息、应用编码字节、TCP 报文段是同一个单位应用可定义消息TCP 提供无应用消息边界的字节流报文段是 TCP 传输结构TCP 想发多少数据只看拥塞窗口还需接收窗口、MSS、路径限制、首部开销、实际待发送数据等write()一次 200000 字节 → 立即发出固定 137 个满载段137 只是给定 1460 载荷条件下的计算示例真实行为受发送状态等影响IPv4 最大 65535所以链路也必须能承载 65535IP 格式上限与每段链路 MTU 是不同限制发送端已准确知道当前真实 PMTU但还能遇到更小的同一路径 MTU在路径与封装条件保持不变时不能这样推断问题来自已知值偏差或路径变化等IPv4 分片是把原始 IP 首部切小每片各有完整 IPv4 首部原始载荷按 IP 分片规则拆分TCP SEQ 能替代 IPv4 Fragment OffsetTCP SEQ 标记连接某方向的字节流位置IP Offset 标记一个原 IP 数据报载荷内的位置IP 源地址就是最后一跳物理网卡不是源 IP 为头部逻辑地址物理收包接口由 OS 接收路径上下文确定目的 IP 必然等于实际物理入站接口上配置的 IP不总成立依主机多接口配置和接收规则而异IP 层看到 TCP 目的端口后就直接选择 Java 进程IP 根据 Protocol 交给 TCPTCP 综合 IP 元数据/端口/连接状态找到 Socket 接收路径同一 TCP 端口只能对应一个 Socket监听 Socket 与多条已连接 Socket 可共享服务器本地端口两个独立监听的绑定规则另判两个已建连接的四元组不同所以两个进程可以随便绑定相同监听端点已建连接识别与监听端点绑定是两类不同机制ECN 路由器标 CE 后会自动直接向发送方报告典型经典 TCP ECN 是中间设备正向标记接收端观察后通过 TCP 向发送端反馈IPv4 首部校验通过就意味着整个 TCP 数据可靠送达首部校验只检查 IPv4 首部可靠字节流由其他机制提供业务完成另行判断