一、初识协议及分层

  1. 国际标准化组织

    • IEEE(电气和电子工程师协会)是技术专家组成的组织。它制定了大量通信协议相关标准,比如 IEEE 802 系列标准,覆盖局域网到广域网技术。

    • ISO(国际标准化组织)由多国标准化团体组成。它制定了 OSI 七层网络模型,该模型在理论研究中影响广泛。

    • ITU(国际电信联盟)是联合国下属机构。它负责制定电信领域国际标准,与 ISO 合作保障通信技术的全球兼容性。

  2. 区域标准化组织

    • ETSI(欧洲电信标准学会)由欧洲各国资助。它由电信行业厂商和研究机构参与,从事通信标准制定工作。

    • ASTAP(亚洲与泛太平洋电信标准化协会)由日韩发起成立。它旨在推动亚太地区信息通信基础设施的标准化协作。

  3. 民间国际团体

    IETF(互联网工程师任务组)是志愿组织。它负责开发和推广 TCP/IP 协议族相关标准,通过 RFC 文档发布协议规范。

  4. 官方机构

    FCC(联邦通信委员会)是美国通信管理机构。它负责审查通信产品的技术特性,对通信标准化工作进行监督。

  5. 企业主体

    部分企业会自研协议栈并制定相关标准,用于低功耗蓝牙、Zigbee、Thread、Matter 等技术领域。

注意:协议本质属于软件。协议采用分层结构,目的是实现模块化设计和解耦合。

层级 核心职责 关键说明与典型设备 / 协议
物理层 负责光 / 电信号的传递方式 定义传输介质(网线、光纤、同轴电缆、无线电磁波),决定传输速率、距离和抗干扰性。典型设备:集线器(Hub
数据链路层 负责设备间数据帧的传送与识别 包含帧同步、冲突检测、差错校验等工作,有以太网、无线 LAN 等标准。典型设备:交换机(Switch)
网络层 负责地址管理与路由选择 通过 IP 地址标识主机,依靠路由表规划传输路径。典型设备:路由器(Router)核心协议:IP
传输层 负责两台主机间的数据传输 确保数据可靠送达,提供端到端的通信服务。核心协议:TCP(可靠)、UDP(不可靠)
应用层 负责应用程序间的沟通 为各类应用提供协议支持,网络编程主要针对此层。典型协议:SMTP、FTP、Telnet

二、再识协议

2.1 为什么要有TCP/IP协议?

单机环境中,设备通信依赖本地协议(如 SATA、IDE),这些协议运行在主机硬件内部,通信距离近、问题少。

网络通信中,主机之间距离变远,通信面临路由寻址、数据丢失、传输效率等新问题,需要专门的协议栈解决。

TCP/IP 协议通过分层设计,针对不同问题提供对应的解决方案,以保障跨主机通信的稳定与可靠。

2.2 什么是TCP/IP协议?

 TCP/IP协议的本质是一种解决方案 ,TCP/IP协议能分层,前提是因为问题们本身能分层。

2.3 所以究竟什么是协议?

问题:主机B能识别data,并且准确提取a=10,b=20,c=30吗?

回答:答案是肯定的!因为双方都有同样的结构体类型struct protocol。用同样的代码实现协议,用同样的自定义数据类型,天然就具有"共识",能够识别对方发来的数据。

关于协议的朴素理解:所谓协议,就是通信双方都认识的结构化的数据类型。

因为协议栈是分层的,所以,每层都有双方都有协议,同层之间,互相可以认识对方的协议。

三、网络传输基本流程

3.1 局域网络传输

  • 以太网的介质访问控制规则共享式以太网中,同一碰撞域内的设备,任何时刻只允许一台设备发送数据。如果多台设备同时发送数据,会发生信号干扰,这种情况叫数据碰撞。发送数据的主机需要通过 CSMA/CD 机制完成碰撞检测与碰撞避免。

  • 碰撞域的定义没有交换机的情况下,整个以太网属于同一个碰撞域。交换机的每个端口会划分独立的碰撞域,以此减少网络中的碰撞概率。

  • 局域网的报文接收判定规则主机在局域网通信中,会通过目标 MAC 地址判断收到的报文是否是发给自己的。主机只会接收目标 MAC 地址与自身网卡 MAC 地址匹配的报文,丢弃不匹配的报文。

3.1.1 局域网(以太网为例)通信原理

 两台主机互联:

具体每层:对应就是入栈和出栈的过程。

  • 网络分层通信的逻辑模型,被理解为同层协议之间直接通信。发送方的数据会从上层到下层逐层封装,接收方的数据会从下层到上层逐层解包,每层只和对方的同层协议进行逻辑交互。

  • 数据封装的本质,是每一层在收到上层数据后,加上本层的协议报头。这个过程可以类比为填充结构体变量,每层的报头包含协议处理所需的控制信息。

  • 接收方的核心操作都必须完成两件事:

    • 第一件事:将本层的协议报头和有效载荷(上层数据)分离。

    • 第二件事:根据报头中标识上层协议的字段,将有效载荷交付给对应的上层协议处理。

  • 协议的共性规则所有层的协议,报头中都必须包含一个字段,用来指明自己的有效载荷需要交付给上层的哪一个协议。

  • 报文结构定义报文由两部分组成:报头和有效载荷。报头是对应协议层的结构体字段,有效载荷是除报头外的数据部分。所以报文的构成可以表示为:报文 = 报头 + 有效载荷。

  • 不同层级的报文名称不同协议层对数据包有不同称谓:传输层的报文叫(segment),网络层的报文叫数据报(datagram),数据链路层的报文叫(frame)。

  • 封装与解封装过程应用层数据通过协议栈发送时,每层协议都会添加数据首部(header),这个过程叫封装。首部包含首部长度、载荷长度、上层协议类型等信息。数据封装成帧后在传输介质中传递,到达目标主机后,每层协议会剥掉对应的首部,再根据首部中的 “上层协议字段”,把数据交给对应的上层协议处理。

3.1.2 认识MAC地址

  • 作用:MAC 地址是数据链路层中,用于识别相连节点的唯一标识。

  • 规格:MAC 地址长度为 48 比特(6 个字节),通常以十六进制数字加冒号的形式表示,例如 08:00:27:03:fb:19

  • 特性:MAC 地址一般在网卡出厂时固化,具有全球唯一性;虚拟机的 MAC 地址为模拟地址,可能出现冲突;部分网卡支持用户修改 MAC 地址。

  • 查看方式:在 Windows 系统中,可通过 ipconfig /all 命令查看本机 MAC 地址。

3.1.3 数据包封装和分用

数据封装过程:

数据分用过程:

3.2 跨网络传输流程图

3.2.1 网络中的地址管理 - 认识IP地址

  • P 协议分为 IPv4 和 IPv6 两个版本,若无特殊说明,默认指代 IPv4 协议。

  • IP 地址是 IP 协议中,用于标识网络内不同主机的地址。

  • IPv4 地址是一个 4 字节(32 位)的整数。

  • IPv4 地址通常用 “点分十进制” 字符串表示,例如 192.168.0.1,每个用点分隔的数字对应 1 个字节,取值范围为 0-255。

跨网段的主机的数据传输:

  • 碰撞域的定义以太网、令牌环网这类共享介质的网络,存在 “碰撞域” 概念。同一碰撞域内,任何时刻只允许一台主机发送数据,否则就会出现信号干扰,导致数据无法正常传输。

  • 数据碰撞的产生与处理当多台主机在同一碰撞域内同时发送数据时,会发生数据碰撞。主机可通过物理信号识别到碰撞事件,随后通过碰撞检测(CSMA/CD)碰撞避免机制,执行退避算法,等待一段时间后再重新发送数据,以此解决冲突问题。

IP地址的意义:

结合封装与解包:

  • IP 地址与 MAC 地址的路由行为数据在跨网络路由时,源 IP 地址和目的 IP 地址在整个传输过程中通常保持不变(NAT 场景除外);而数据链路层的源 MAC 地址和目的 MAC 地址会在每一跳转发时被重写,用于标识当前局域网的直接收发设备。

  • 二者的作用定位目的 IP 地址是网络层的逻辑标识,它代表数据传输的最终目标,是路由器进行路径选择的核心依据;MAC 地址是数据链路层的物理标识,它仅在同一局域网内有效,是局域网内转发数据帧的直接依据,只负责数据在 “当前一跳” 的传递。

提炼IP网络的意义和网络通信的宏观流程:

IP网络层存在的意义:提供网络虚拟层,让世界的所有网络都是 IP 网络,屏蔽最底层网络的差异。
 

四、Socket 编程预备

4.1 理解源IP地址和目的IP地址

IP 地址在网络中用于唯一标识一台主机。

数据传输的最终目的不是到达主机,而是交付给主机内的对应进程。

进程是用户在操作系统中的执行载体,用户通过进程完成聊天、下载、浏览等网络行为。

主机内存在多个进程,仅靠 IP 地址无法将数据精准转发到目标进程,因此需要额外标识区分进程。

4.2 认识端口号

  1. 数据规格:端口号是一个 16 位(2 字节)的无符号整数,取值范围为 0~65535。

  2. 核心作用:端口号用于标识主机内的进程,它告诉操作系统数据应交付给哪个进程处理。

  3. 完整标识:IP 地址 + 端口号可以唯一标识网络上某台主机中的特定进程。

  4. 占用规则:一个端口号在同一主机的同一传输层协议下,只能被一个进程占用。

4.2.1 端口号范围划分

  • 0-1023:知名端口号,HTTP、FTP、SSH 等常用应用层协议使用固定端口号。

  • 1024-65535:动态分配端口号,客户端程序的端口号由操作系统从该范围分配。

4.2.2 理解"端口号"和"进程ID"

  • 进程 PID 是系统层面的概念,可唯一标识进程,但系统未直接用它做网络标识,避免进程管理与网络功能过度耦合。

  • 端口号是网络层面的标识,一个进程可绑定多个端口号,但一个端口号只能被一个进程绑定。

4.2.3 理解源端口号和目的端口号

传输层(TCP/UDP)的数据段包含源端口号和目的端口号,分别标识数据的发送进程和接收进程。

4.2.4 理解socket

  • P 地址标识互联网中的一台主机,端口号标识主机内的一个网络进程。

  • IP + 端口号(socket) 可唯一标识互联网中的一个进程。

  • 网络通信的本质是进程间通信,{源IP, 源端口, 目的IP, 目的端口} 四元组可唯一标识一次网络通信。

  • socket 字面意为 “插座 / 接口”,在网络编程中代表网络通信的端点。

4.3 传输层的典型代表

TCP 协议基础特性:TCP(传输控制协议 Transmission Control Protocol )是传输层的协议。

:传输层协议 ;有连接 ;可靠传输 ;面向字节流。

UDP 协议基础特性:UDP(用户数据报协议 User Datagram Protocol)是传输层的协议。

:传输层协议;无连接;不可靠传输;面向数据报。

4.4 网络字节序

网络数据流存在字节序问题。发送主机按内存地址从低到高的顺序发送数据。接收主机按内存地址从低到高的顺序保存数据。因此,网络数据流定义为先发出的数据为低地址,后发出的数据为高地址。

TCP/IP 协议规定,网络数据流统一采用大端字节序,即低地址存放高字节。所有主机无论本身是大端还是小端,发送和接收数据都必须遵循这一规定。小端主机发送数据前需将数据转换为大端格式,大端主机则无需转换。

为保证网络程序的跨平台可移植性,C 语言提供了专用的字节序转换函数,包含在<arpa/inet.h>头文件中:

  • htonl:将 32 位长整数从主机字节序转为网络字节序

  • htons:将 16 位短整数从主机字节序转为网络字节序

  • ntohl:将 32 位长整数从网络字节序转为主机字节序

  • ntohs:将 16 位短整数从网络字节序转为主机字节序

这些函数会根据主机实际字节序自动处理转换。如果主机是小端字节序,函数会完成大小端转换;如果主机是大端字节序,函数直接返回原数据。网络通信的统一规定是:所有发送到网络上的数据,都必须是大端格式。

4.5 socket 编程接口

4.5.1 socket 常见API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);

// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);

// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);

// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • socket() 函数这个函数用于创建一个 socket 文件描述符,支持 TCP 和 UDP 协议,客户端和服务器端都需要调用它。它接收三个参数:通信域(如 IPv4/IPv6)、套接字类型(如 TCP 的 SOCK_STREAM、UDP 的 SOCK_DGRAM)、协议号(通常为 0)。

  • bind() 函数这个函数用于将创建好的 socket 文件描述符,绑定到指定的 IP 地址和端口号上,主要由服务器端调用。它接收 socket 文件描述符、sockaddr类型的地址结构体指针、地址长度三个参数。

  • listen() 函数这个函数用于将 socket 设置为监听状态,仅 TCP 服务器端调用。它接收 socket 文件描述符和 backlog 参数,backlog 表示等待连接队列的最大长度。

  • accept() 函数这个函数用于从已完成连接的队列中取出一个客户端连接请求,仅 TCP 服务器端调用。它会返回一个新的文件描述符,用于和客户端通信,同时会填充客户端的地址信息。

  • connect() 函数这个函数用于客户端主动向服务器发起连接请求,仅 TCP 客户端调用。它接收 socket 文件描述符、服务器地址结构体指针、地址长度三个参数。

4.5.2 sockaddr结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同.

  • 地址结构体定义IPv4 和 IPv6 的地址格式定义在头文件netinet/in.h中。IPv4 地址用sockaddr_in结构体表示,它包含 16 位地址类型、16 位端口号和 32 位 IP 地址。

  • 地址类型标识IPv4 和 IPv6 的地址类型分别用常量AF_INETAF_INET6表示。程序可以通过sockaddr结构体首地址中的地址类型字段,判断其具体地址类型,无需提前知道结构体细节。

  • Socket API 的通用设计Socket API 统一使用struct sockaddr *类型接收地址参数。编程时需要将具体的地址结构体(如sockaddr_in)强制转换为该通用类型。这种设计提升了程序的通用性,让接口可以兼容 IPv4、IPv6 和 UNIX Domain Socket 等多种地址类型。

4.5.3 in_addr结构

  • in_addr_tuint32_t 的别名,代表 32 位无符号整数。

  • struct in_addr 仅包含 in_addr_t 类型的 s_addr 成员。

  • 该结构体的作用是存储 IPv4 地址的二进制数值,数据为网络字节序。

五、Socket编程UDP

5.1 UDP网络编程

5.2 V1版本 - Echo server

5.3 V2 版本 - DictServer

5.4 V2 版本 - DictServer封装版

5.5  V3版本 - 简单聊天室

5.6  补充参考内容

地址转换函数

关于inet_ntoa

remove_if 样例

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐