1.1 引言
不同厂家生产运行不同操作系统的计算机,却能通过TCP/IP协议族通信。TCP/IP起源于60年代美国政府资助项目,90年代成为计算机间常用组网形式,是开放系统,构成"全球互联网"基础,广域网已连接超100万台计算机。网络发展从孤立计算机到局域网,再到多个网络相连形成互联网(internet),即通过相同协议族互连的网络。构造互联网简单方式是用路由器连接不同网络,路由器可连接以太网、令牌环网等物理网络 。历史上路由器曾称网关,现网关表示应用层网关,连接不同协议族。网络可分为端系统(主机)和中间系统(路由器),应用层和运输层用端到端协议,网络层提供逐跳协议。
互联网优势在于应用层可隐藏物理网络细节,不同物理网络增加不影响应用层,物理细节隐藏使互联网功能强大。
连接网络方式有网桥和路由器,网桥在链路层互连网络,路由器在网络层互连。
1.2 分层
网络协议分层开发,每层负责不同通信功能,TCP/IP是不同层次多个协议组合,通常被认为是四层协议系统:
- 链路层:也称数据链路层或网络接口层,包含操作系统设备驱动程序和计算机中对应网络接口卡,处理与电缆等物理媒介连接细节。
- 网络层:选路,协议包括IP、ICMP、IGMP 。
- 运输层:为应用程序提供端到端通信,有TCP和UDP 。
- 应用层:常见应用程序有Telnet、FTP、e - mail等。


1.3 TCP/IP的分层及相关协议
运输层协议
- TCP:运输层著名协议,使用不可靠的IP服务,却提供可靠运输层服务。
- UDP:为应用程序发送数据报,不可靠,不保证数据报安全到达。
网络层协议
- IP:网络层主要协议,被TCP和UDP使用,负责在互联网中传输数据。
- ICMP:IP协议附属协议,用于主机或路由器交换错误报文等重要信息。
- IGMP:Internet组管理协议,用于将UDP数据报多播到多个主机。
链路层相关协议
- ARP(地址解析协议):用于某些网络接口(如以太网、令牌环网),转换IP层和网络接口层地址。
- RARP(逆地址解析协议):同样用于上述网络接口进行地址转换,5章分析介绍。

互联网的地址
IP地址基本信息
- 互联网上每个接口需有唯一的32位Internet地址(IP地址) ,不采用平面地址空间,常以点分十进制表示法呈现,如140.252.13.33 。
IP地址分类及范围
- 分为五类,区分方法看第一个十进制整数。A类:0.0.0.0 - 127.255.255.255;B类:128.0.0.0 - 191.255.255.255;C类:192.0.0.0 - 223.255.255.255;D类:224.0.0.0 - 239.255.255.255;E类:240.0.0.0 - 247.255.255.255 。
IP地址管理
- 多接口主机每个接口对应一个IP地址。因每个接口需唯一IP地址,由互联网络信息中心(InterNIC )分配网络号,系统管理员分配主机号。
IP地址类型
- 有单播地址(目的为单个主机)、广播地址(目的为给定网络上所有主机)、多播地址(目的为同一组内所有主机) ,12章和13章将讨论广播和多播细节。后续介绍IP选路后会引入子网概念,特殊IP地址(主机号和网络号全0或全1 )。

1.5 域名系统
- 虽可通过IP地址识别主机网络接口,但人们更常用主机名。在TCP/IP领域,域名系统(DNS)是分布式数据库,用于提供IP地址和主机名之间的映射信息,第14章将详细讨论。应用程序可调用标准库函数,实现给定主机名查IP地址,或给定IP地址查主机名。多数以主机名作为参数的应用程序,也能以IP地址作为参数,如用Telnet远程登录时可指定主机名或IP地址。
1.6 封装
- TCP数据封装:应用程序用TCP传送数据时,数据送入协议栈,逐层传递并在每层增加首部(有时还有尾部)信息,最终以比特流送入网络。TCP传给IP的数据单元是TCP报文段(TCP segment),IP传给网络接口层的数据单元是IP数据报(IP datagram) ,通过以太网传输的比特流是帧(Frame) 。以太网数据帧物理特性要求长度在46 - 1500字节。IP和网络接口层间传送的数据单元是分组(packet),分组可以是IP数据报或其分片(fragment)。
- UDP数据封装:UDP数据与TCP数据类似,UDP传给IP的信息单元是UDP数据报(UDP datagram) ,UDP首部为8字节。
- 协议标识:因TCP、UDP、ICMP和IGMP都向IP传送数据,IP在首部的协议域(8bit)存入数值标识数据所属协议,1为ICMP ,2为IGMP ,6为TCP ,17为UDP 。运输层协议(TCP和UDP)用16bit端口号标识不同应用程序,在报文首部存入源端口号和目的端口号。网络接口层发送和接收IP、ARP和RARP数据时,以太网帧首部的16bit帧类型域用于标识生成数据的网络层协议。

1.7 分用
- 分用过程:目的主机收到以太网数据帧后,数据从协议栈由底向上传输,同时去除各层添加的报文首部。每层协议依据首部协议标识,确定接收数据的上层协议,此过程即分用。以太网驱动程序根据帧首部帧类型分用,将数据交给ARP、RARP或IP ;IP根据首部协议值分用,把数据交给ICMP、IGMP、TCP或UDP ;TCP和UDP根据首部端口号,将数据交给对应的应用程序。
- 协议定位难题:ICMP和IGMP是IP附属协议,在不同情境下层级定位有差异,有时与IP同层,有时在IP层之上,因其报文封装在IP数据报中。ARP和RARP也类似,因和IP数据报有各自以太网数据帧类型,有时在以太网设备驱动程序上方,有时在IP层下方(从逻辑角度) ,体现出分层协议存在不完美性。后续描述TCP细节时,会发现协议通过目的端口号、源IP地址和源端口号进行解包。

1.8 客户 - 服务器模型
- 模型概述:多数网络应用程序编写时假定一端为客户,一端为服务器,服务器为客户提供特定服务。
- 服务类型
-
- 重复型服务器:交互步骤为等待客户请求(I1)、处理请求(I2)、发送响应(I3)、返回等待(I4) 。问题在于处理请求(I2)时无法为其他客户服务。
- 并发型服务器:步骤是等待客户请求(C1)、启动新服务器处理请求(C2,可能生成新进程、任务或线程,依赖操作系统 )、处理完终止新服务器并返回等待(C3) 。优点是可同时为多个客户服务。一般TCP服务器并发,UDP服务器重复。
1.9 端口号
- 端口号作用及选择:TCP和UDP用16bit端口号识别应用程序。服务器多通过知名端口号识别,如FTP服务器TCP端口号21、Telnet服务器TCP端口号23、TFTP服务器UDP端口号69 。1 - 1023之间知名端口号由IANA管理 ,1 - 255为早期知名端口,256 - 1023曾多被Unix系统占用提供特定服务。
- 客户端端口号:客户端一般不关心所用端口号,只需保证在本机唯一,称临时端口号(存在短暂,用户运行程序时存在 )。多数TCP/IP实现给临时端口分配1024 - 5000之间端口号 ,大于5000为其他服务器预留。Solaris 2.2例外,TCP和UDP缺省临时端口号从32768开始。可通过系统文件/etc/services查看熟知端口号,如用grep命令查找Telnet和域名系统端口号。
- 保留端口号:Unix系统有保留端口号概念,1 - 1023之间,只有超级用户特权进程可分配,部分应用程序(如Rlogin )将其用于客户与服务器身份认证。
1.12 标准的简单服务
有一些标准简单服务在多数实现中都会提供 ,本书会使用部分服务程序,客户程序常选Telnet。当TCP和UDP提供相同服务时,一般选用相同端口号,具体如下:
|---------|--------|--------|-----|--------------------------------------------------|
| 名字 | TCP端口号 | UDP端口号 | RFC | 描述 |
| echo | 7 | 7 | 862 | 服务器返回客户发送的所有内容 |
| discard | 9 | 9 | 863 | 服务器丢弃客户发送的所有内容 |
| daytime | 13 | 13 | 867 | 服务器以可读形式返回时间和日期 |
| chargen | 19 | 19 | 864 | 客户发送数据报时,TCP服务器发送一串连续字符流直至客户中断连接;UDP服务器发送随机长度数据报 |
| time | 37 | 37 | 868 | 服务器返回一个二进制形式的32bit数,表示从UTC时间1900年1月1日午夜至今的秒数 |
🔷习题:什么是 TCP/IP 协议的稳健性原则?
稳健性原则:"在实现时,发送的数据要严格遵循规范,接收数据时要尽量宽容"。
在 RFC 1122 中,这一原则被进一步应用于 TCP/IP 协议族的每一层,确保主机实现能够在不可靠的网络环境中稳定运行。
稳健性原则的核心思想
发送端严格:确保发送的数据完全符合协议规范,减少接收端的处理负担。
接收端宽容:允许接收端处理非标准但可接受的数据(如忽略未知选项、处理乱序数据),以提高系统在复杂网络环境中的鲁棒性。
应用于 TCP/IP 协议族每一层的稳健性原则
链路层
- 稳健性原则:主机必须能够发送和接收 RFC 894 定义的以太网封装格式的分组,并应支持与 RFC 894 混合的 RFC 1042(IEEE 802)封装格式的分组,可能还能发送 RFC 1042 格式的分组。链路层实现需要处理不同网络接口的 MTU(最大传输单元)大小,并正确处理帧校验序列(FCS)以检测传输错误。
稳健性要求主机能宽容地处理不同封装格式的分组,并在发送时严格遵循标准格式,以确保与不同网络设备的互操作性。
网络层(IP 层)
- 稳健性原则:IP 层提供无连接、不可靠、尽最大努力交付的服务。主机必须能够处理 IP 数据报的分片和重组,丢弃无效数据报并发送 ICMP 错误消息给源端,同时宽容地接受可能出现的乱序或重复数据报。主机还需支持动态路由协议(如 RIP)以适应网络拓扑变化。稳健性体现在接收端对数据报的宽容处理(如忽略未知选项、处理分片乱序)和发送端对标准头部格式的严格遵循,以保证在异构网络中的可靠通信。
传输层(TCP 和 UDP)
TCP 稳健性原则:TCP 提供面向连接、可靠的字节流传输。主机实现必须严格发送符合 RFC 793 规范的 TCP 报文段(包括正确设置序列号、确认号、校验和等),并在接收时宽容处理异常情况,如重复的 SYN 包、错误的校验和或意外的 RST 包。TCP 还需支持滑动窗口、超时重传、拥塞控制等机制以应对网络不可靠性。
- 具体要求包括:
-
- 正确处理"三次握手"和"四次挥手"以建立和终止连接。
- 使用序列号和确认号确保数据不丢失、不重复且按序交付。
- 实现拥塞控制算法(如慢启动、拥塞避免、快速重传和快速恢复)以适应网络拥堵。
UDP稳健性原则:UDP 提供不可靠的数据报服务。主机必须严格发送包含正确校验和的 UDP 数据报,但在接收时可选择忽略校验和错误(除非明确要求验证)以提高兼容性。主机还需处理多宿主环境下的地址验证问题。
🔷 习题:什么是 RFC?RFC 的由来 是什么?
RFC 是 "Request for Comments" 的缩写,中文通常翻译为"请求评论"或"征求意见"。它是一种正式的文档,用于描述互联网的技术和组织过程。这些文档旨在确保互联网技术标准的公开讨论,并鼓励全球范围内的专家提出意见和建议。通过这样的方式,可以促进互联网技术和政策的发展。RFC 成为了互联网标准制定过程中不可或缺的一部分。
🌍思考:IP 层接受到乱序的 IP 数据包应该怎么办?还是说 IP 数据包根本没有乱序这个概念?
IP 协议的基本特性
IP(Internet Protocol,定义于 RFC 791)是 TCP/IP 协议族网络层的主要协议,提供无连接、不可靠、尽最大努力交付的服务。其核心特性包括:
- 无连接性:IP 数据报(datagram)是独立发送的,每个数据报包含完整的源地址和目的地址信息,路由器根据目标地址逐跳转发。
- 不可靠性:IP 不保证数据报按序到达、完整到达或不丢失。它只负责将数据报尽力送达目的主机。
- 分片与重组:当数据报大小超过链路层 MTU(最大传输单元)时,IP 层会对数据报进行分片(fragmentation),并在目的主机进行重组(reassembly)。
基于这些特性,IP 协议本身并不维护数据报的顺序,也不保证数据报按发送顺序到达目的主机。因此,乱序(out-of-order)是 IP 协议的固有特性,在设计上,IP 层允许数据报以任意顺序到达,甚至可能出现重复或丢失。
IP 数据包是否会乱序?
在 Internet 中,数据报可能通过不同的路由路径到达目的主机。例如,路由器可能根据网络负载或链路状态选择不同路径,导致后发送的数据报先到达。某些数据报可能因网络拥塞、链路故障或路由变化而延迟,导致到达顺序与发送顺序不一致。
分片传输:当数据报被分片后,不同分片可能通过不同路径到达目的主机,导致分片到达顺序不一致。
因此,乱序是 IP 层的常态,IP 协议本身并不提供机制来纠正乱序。乱序的处理通常由上层协议(如 TCP)负责。
IP 层接收到乱序数据包的处理方式
根据 RFC 1122(主机需求)和 RFC 791(IP 协议规范),IP 层在接收数据包时的处理方式遵循稳健性原则("发送时严格,接收时宽容")。具体到乱序数据包,IP 层的职责和行为如下:
完整数据报(未分片):
- IP 层不对数据报的顺序做任何假设或处理。接收到的每个数据报被视为独立单元,IP 层会检查数据报的头部(包括版本、长度、校验和等),验证其有效性。
- 如果数据报有效(校验和正确、无畸形头部等),IP 层将其传递给上层协议(例如 TCP 或 UDP),而不关心其到达顺序。
- 如果数据报无效(例如校验和错误、头部格式错误),IP 层会丢弃该数据报,并可能发送 ICMP 错误消息(如"参数问题")给源端。
- 结论:对于未分片的数据报,IP 层不处理乱序问题,直接将有效数据报传递给上层协议,由上层协议(如 TCP)处理顺序问题。
分片数据报:
- 当数据报被分片后,IP 层在目的主机负责将所有分片重组为完整的数据报。RFC 791 和 RFC 1122 明确要求主机实现 IP 分片重组功能。
- 分片重组过程:
-
- 标识字段(Identification):IP 数据报头部中的标识字段用于标识属于同一个原始数据报的分片。所有分片共享相同的标识值。
- 分片偏移(Fragment Offset):指示每个分片在原始数据报中的相对位置(以 8 字节为单位)。
- 标志(Flags):包括"更多分片(More Fragments, MF)"位,指示是否还有后续分片。
- 重组逻辑:
-
-
- 主机维护一个重组缓冲区,将接收到的分片按标识字段分组。
- 根据分片偏移字段,将分片放置在正确的位置。
- 当所有分片(包括 MF=0 的最后一个分片)都到达,且没有缺失或重叠时,主机将分片重组为完整数据报,并传递给上层协议。
- 如果在重组超时(通常为 60 秒,参考 RFC 1122)内未收集到所有分片,主机丢弃已接收的分片,并可能发送 ICMP"分片重组超时"错误消息。
-
- 乱序分片的处理:
-
-
- IP 层对分片的到达顺序没有要求,可以宽容地处理乱序到达的分片。只要所有分片在超时时间内到达,主机就能正确重组。
- 主机必须能够处理重复分片(丢弃重复分片)和部分重叠分片(根据偏移量选择性地覆盖或丢弃)。
-
结论:对于分片数据报,IP 层通过分片重组机制间接处理乱序问题,但重组过程不依赖分片的到达顺序,仅依赖标识字段和偏移字段。
乱序处理的责任归属
虽然 IP 层能够通过分片重组机制处理分片的乱序到达,但对于完整数据报的乱序,IP 层不承担任何排序责任。这一职责由上层协议处理:
TCP:
- TCP 是面向连接的可靠传输协议,使用序列号(Sequence Number)和确认号(Acknowledgment Number)来重新排序乱序到达的数据报,丢弃重复数据,并请求重传丢失数据。
- TCP 的滑动窗口机制和重传机制确保数据按正确顺序交付给应用层。
- 因此,IP 层的乱序数据报对 TCP 来说是透明的,TCP 会重新组织数据流。
为什么 IP 层不直接处理乱序?
IP 层设计为简单、无状态的协议,目的是在异构网络中提供高效、灵活的传输。添加顺序维护机制会增加复杂性,降低性能。
网络层不依赖于预先建立的连接或固定的路径,这使得它能够非常灵活地适应网络拓扑的变化和不同类型的流量需求。例如,当出现新的通信需求时,它可以立即开始传输数据报,而不需要首先建立连接。此外,它还可以动态调整路由以响应网络状况的变化。
🌍 思考:使用 linux socket 编程,socket 的 send 和 receive 的数据是在哪一层?
Linux socket 编程通过 socket API(如 socket()
, send()
, recv()
, sendto()
, recvfrom()
等)提供用户态程序与内核网络协议栈交互的接口。
Linux socket 编程支持多种 socket 类型,主要涉及 TCP/IP 协议族的传输层和网络层。
- SOCK_STREAM:基于 TCP,提供面向连接的、可靠的字节流传输(传输层)。
- SOCK_DGRAM:基于 UDP,提供无连接的、不可靠的数据报传输(传输层)。
send
和 receive
(或 sendto
和 recvfrom
)操作的协议层取决于 socket 的协议类型。以下逐一分析:
1. TCP Socket(SOCK_STREAM)
协议层:传输层(TCP,Layer 4)。
数据处理:
- send :当应用程序调用
send()
或write()
发送数据时,数据被写入用户态的 socket 缓冲区,随后由内核的 TCP 协议栈接管。TCP 层将数据分段(segmentation),为每个 TCP 报文段添加头部(包括序列号、确认号、窗口大小等),并传递给网络层(IP 层)进行封装和发送。 - receive :当内核接收到 TCP 报文段时,TCP 层验证序列号、确认号和校验和,重新排序乱序报文段,处理重传和拥塞控制后,将重组的字节流存入 socket 的接收缓冲区。应用程序通过
recv()
或read()
从接收缓冲区读取数据。
数据所在层次:
send
:应用程序提供的是应用层数据 (字节流),但send
操作将数据交给内核的 TCP 层 处理。数据在 TCP 层被组织为报文段。receive
:应用程序从 socket 缓冲区读取的是 TCP 层重组后的字节流,已经是去除了 TCP 头部的应用层数据。
总结 :对于 TCP socket,send
和 receive
操作的数据在 传输层(TCP) 处理,应用程序操作的是接近应用层的字节流,但实际数据由 TCP 协议栈管理。
2. UDP Socket(SOCK_DGRAM)
- 协议层:传输层(UDP,Layer 4)。
- 数据处理:
-
- sendto (或
send
):应用程序调用sendto()
或send()
(对于已连接的 UDP socket)发送数据时,数据被视为一个完整的数据报(datagram)。内核的 UDP 协议栈为数据报添加 UDP 头部(包括源端口、目的端口、长度、校验和),然后传递给 IP 层进行封装和发送。 - recvfrom (或
recv
):当内核接收到 UDP 数据报时,UDP 层验证校验和(如果启用),去除 UDP 头部后,将数据报存入 socket 的接收缓冲区。应用程序通过recvfrom()
或recv()
读取完整的数据报。
- sendto (或
- 数据所在层次:
-
sendto
:应用程序提供的是应用层数据(数据报),UDP 层将其封装为 UDP 数据报,交给 IP 层。recvfrom
:应用程序读取的是 UDP 层去除了头部后的数据报,本质上是应用层数据。
对于 UDP socket,send
和 receive
操作的数据在 传输层(UDP) 处理,应用程序操作的是数据报形式的 UDP payload。
Linux 内核中的数据流
在 Linux 内核中,send
和 receive
操作涉及用户态和内核态之间的数据交互:
- 发送流程:
-
- 应用程序通过
send()
或sendto()
将数据写入 socket 的发送缓冲区(用户态到内核态)。 - 内核的协议栈(TCP 或 UDP)对数据进行封装(添加传输层头部),然后交给 IP 层(添加 IP 头部),最终通过链路层发送到网络。
- 应用程序通过
- 接收流程:
-
- 网络接口接收数据包,内核的链路层去除帧头部,传递给 IP 层。
- IP 层验证数据包并进行分片重组(如果需要),然后根据协议号分发给 TCP 或 UDP 层。
- TCP/UDP 层处理后,将数据存入 socket 的接收缓冲区,应用程序通过
recv()
或recvfrom()
读取。