
◆ 博主名称: 小此方-CSDN博客
大家好,欢迎来到小此方的博客。
⭐️网络系列个人专栏:
【主题曲】计算机网络
⭐️此方的GitHub:
github_此方
⭐️
我们思考 (Rethink) · 我们重建 (Rebuild) · 我们记录 (Record)
文章目录
- 概要&序論
- 零、端口号的底层实现原理补充
-
- 0.1 端口号到进程(PCB)的映射机制
-
- 0.1.1 底层哈希表查找
- 0.2 有效载荷传递给进程的机制
-
- 0.2.1 Linux“一切皆文件”的设计思想
- 一、Socket编程基础认知
-
- 1.1 理解 Socket 与网络通信本质
-
- 1.1.1 唯一标识与进程间通信
- 1.1.2 什么是 Socket
- 1.2 系统层与传输层接口
-
- 1.2.1 传输层系统调用
- 1.3 传输层典型协议简介
-
- 1.3.1 TCP 协议(Transmission Control Protocol)
- 1.3.2 UDP 协议(User Datagram Protocol)
- 二、网络字节序与主机字节序
-
- 2.1 字节序的基本概念
-
- 2.1.1 大端与小端
- 2.2 TCP/IP 网络字节序规定
- 2.3 字节序转换 API 函数
-
- 2.3.1 命名规范与参数含义
- 三、Socket 编程常用接口与数据结构
-
- 3.1 Socket 常见 API
-
- 3.1.1 核心 API 功能简析
- 3.2 sockaddr 结构体抽象
-
- 3.2.1sockaddr结构
- 3.2.2sockaddr_in 结构
- 3.2.3in_addr结构
- 3.3 sockaddr 结构体的统一设计与协议多态
-
- 3.3.1 两种常见地址结构体对比
- 3.3.2 系统底层的多态判断逻辑
- 3.3.3 面向对象思想与协议家族扩展
概要&序論
Hello大家好,我是此方。本文将从网络通信本质出发,系统讲解 TCP/UDP概念,Socket 常用 API 以及 sockaddr 等核心数据结构,逐步建立完整的 Socket 编程知识体系。
零、端口号的底层实现原理补充
应该放在上一期讲,漏掉了。
0.1 端口号到进程(PCB)的映射机制
0.1.1 底层哈希表查找
- 信息传入与解包:数据信息从网络传入,依次经过链路层、网络层和传输层解包。
- 哈希映射:系统拿着传输层报头中的端口号(如 8080),去内核维护的 hash 表中进行索引查找。
- 链式哈希结构:内核使用链式哈希(链表)结构管理连接。在进程创建并绑定端口时,系统会提前将进程控制块(PCB)根据其绑定的端口号链入 hash 表中。通过端口号索引,即可迅速定位到该端口对应的 PCB。

0.2 有效载荷传递给进程的机制
0.2.1 Linux“一切皆文件”的设计思想
- 内核缓冲区:在 Linux 系统中一切皆文件。Socket 在内核中也表现为文件形式,拥有对应的内核缓冲区。
- 数据交付:根据端口号找到目标进程(PCB)后,内核将解包得到的有效载荷直接拷贝放入该 Socket 对应的内核缓冲区当中。
- 上层读取:上层应用程序通过文件描述符读取该内核缓冲区,即可顺利获取从网络接收到的有效载荷。
一、Socket编程基础认知
1.1 理解 Socket 与网络通信本质
1.1.1 唯一标识与进程间通信
- IP 与 Port 的作用:IP 地址用于在互联网中唯一标识一台主机;port(端口号)用于唯一标识该主机上的一个网络进程。
- IP + Port 组合:IP + Port 能够唯一标识互联网中的某个特定进程。
- 网络通信的本质:通信双方本质上是两个互联网进程代表用户进行交互。由 {srcIp, srcPort, dstIp, dstPort} 构成的四元组,可以唯一标识互联网中进行通信的两个进程。因此,网络通信的本质就是跨主机的进程间通信。
1.1.2 什么是 Socket
- 定义:我们把 IP + Port 的组合称作套接字(socket)。

- 字面含义:socket 原意为“插座、插口”。在网络编程中,它就像是一个插座接口,应用程序通过“插上”这个接口接入网络通信网络。
套接字有网络套接字和本地套接字,我们只讲网络套接字,网络套接字学会了本地套接字自然就会了
1.2 系统层与传输层接口
1.2.1 传输层系统调用
- 传输层属于操作系统内核的一部分。
- 当我们理解了操作系统和网络协议栈后,就会明确:要通过网络协议栈进行通信,必须调用传输层提供的系统调用接口。

1.3 传输层典型协议简介
1.3.1 TCP 协议(Transmission Control Protocol)
TCP 即传输控制协议,具有以下关键特性:
- 传输层协议:工作于 OSI 模型的传输层。
- 有连接:通信前双方必须建立连接。(你先可以短暂的这样理解:打电话,你喂我喂就是在建立连接)
- 可靠传输:提供数据包确认、重传机制,保证数据准确无误到达。
- 面向字节流:数据以无边界的字节流形式传输。(水龙头接水就是面向字节流)
文件流和面向字节流是没有太大区别的,只是表述的方式不一样。
1.3.2 UDP 协议(User Datagram Protocol)
UDP 即用户数据报协议,具有以下关键特性:
- 传输层协议:工作于 OSI 模型的传输层。
- 无连接:发送数据前无需建立连接,直接发送。
- 不可靠传输:尽力而为交付,不保证数据是否丢失或按序到达。
- 面向数据报:以独立的数据报文为单位进行传输,保留数据边界。(收发快递,就是面向数据报)
这两个东西肯定不会这么简单就过去了。对于TCP与UDP协议的详细介绍我放在后面再讲,现在当务之急是知晓概念并立即写出我们的第一个服务器代码。
可靠与不可靠是中性词语,可靠能做更多事情,但是更加复杂,不可靠更加简单。于是TCP和UDP从事与不同的应用场景,两者不能一概而论。(目前有此认识,后面会专门出文章深究)
二、网络字节序与主机字节序
2.1 字节序的基本概念
2.1.1 大端与小端
内存和磁盘文件中的多字节数据均有大端(big-endian)与小端(little-endian)之分。网络数据流同样需要明确定义字节存储与传输的顺序: 我好像讲过,传送门:一篇文章讲透——计算机中的数据存储原理
- 发送端:通常将发送缓冲区中的数据按内存地址从低到高的顺序发送。
- 接收端:把从网络上接收到的字节依次保存到接收缓冲区中,同样按内存地址从低到高的顺序保存。
- 网络数据流规定:先发送的数据是低地址,后发送的数据是高地址。

为什么一定要规定大端?目前没有合理的解释 一个可能的原因:发送过来的数据更加符合人类的阅读习惯。
2.2 TCP/IP 网络字节序规定
- 统一标准:TCP/IP 协议规定,网络数据流应采用大端字节序(即低地址高字节)。
- 强制要求:所有发送到网络上的数据,都必须是大端的!
- 主机适应:不论当前主机是大端机还是小端机,都会按照该规定发送和接收数据:
- 若当前发送主机是小端,需先将数据转换为大端后再发送。
- 若当前发送主机是大端,则忽略转换,直接发送。
2.3 字节序转换 API 函数
为了保证网络程序的可移植性,使同样的 C 代码在大端和小端计算机上编译后都能正常运行,头文件 <arpa/inet.h> 提供了以下转换 API:
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
- 如果主机是小端字节序,这些函数会将参数做相应的大端/小端转换后返回。
- 如果主机是大端字节序,这些函数不做任何转换,直接原封不动地返回参数。
2.3.1 命名规范与参数含义
这么多我怎么记?
- h:代表 host(主机字节序)。
- n:代表 network(网络字节序)。
- l:代表 long(32 位长整数,常用于 IP 地址 转换)。
- s:代表 short(16 位短整数,常用于 Port 端口号 转换)。
三、Socket 编程常用接口与数据结构
3.1 Socket 常见 API
Socket API 是系统层提供的一套抽象网络编程接口。常见的核心 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);
3.1.1 核心 API 功能简析
- socket:用于创建套接字并返回一个文件描述符,是进行一切网络通信的基础入口。
- bind:将创建的套接字与指定的本地地址(IP + Port)绑定在一起,主要用于服务器端。
- listen:将套接字设置为监听模式,准备接受来自客户端的连接请求(仅 TCP 服务器端使用)。
- accept:阻塞等待并接收客户端的连接请求,成功后返回一个新的套接字用于后续通信(仅 TCP 服务器端使用)。
- connect:向指定的服务器端发起连接请求(仅 TCP 客户端使用)。
3.2 sockaddr 结构体抽象
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同。

-
IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.
-
IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
-
socket API都可以用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针作为参数;
3.2.1sockaddr结构

3.2.2sockaddr_in 结构
虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in;这个结构里主要有三部分信息: 地址类型, 端口号, IP地址.
3.2.3in_addr结构
in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数;
3.3 sockaddr 结构体的统一设计与协议多态

3.3.1 两种常见地址结构体对比
在实际使用中,我们根据不同的通信场景定义具体的结构体,再强制类型转换为 struct sockaddr 传给 Socket API:
- 网络通信(IPv4):使用 struct sockaddr_in
- 前 16 位:地址类型(AF_INET)
- 16 位端口号
- 32 位 IP 地址
- 8 字节填充
- 本地通信(UNIX Domain Socket):使用 struct sockaddr_un
- 前 16 位:地址类型(AF_UNIX)
- 108 字节路径名
3.3.2 系统底层的多态判断逻辑
Socket API 在底层接收到强转后的 struct sockaddr 指针后,通过读取结构体最前方的地址家族标识(sin_family)自行区分通信类型:
if (address->sin_family == AF_INET) {
// 底层转化为对应的网络结构体,执行网络通信
} else if (address->sin_family == AF_UNIX) {
// 底层转化为对应的本地结构体,执行本地通信
}
3.3.3 面向对象思想与协议家族扩展
- Socket API 实现了“用什么通信传什么结构体,但使用同一套通用 API 接口”。这种通过头部字段区分类型、使用通用结构体指针转接的设计,是 C 语言层面对面向对象多态思想的经典实现。
- 由于统一套接口的设计,未来即使出现新的通信方式,也只需按规范定义新的结构体并传入系统接口即可,保证了 API 的极高通用性与扩展性。
好的本期内容就到这里,如果对你有帮助,还不要忘记点赞三联支持。我是此方,我们下期再见。bye! Linux、C++、算法持续连载中,欢迎关注WeChat Official Account 【此方的技术栈】。






