
🔥小叶-duck:个人主页
❄️个人专栏:《Data-Structure-Learning》《C++入门到进阶&自我学习过程记录》 《Linux系统从入门到实践》《Linux网络从入门到实践》 《Qt 方寸极境》 《MySQL》
✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游
目录
前言
一、网络通信的本质:从主机寻址到进程间的数据交付
二、端口号:主机内部进程的网络身份唯一标识
2.1 端口号的核心定义
2.2 端口号的范围划分
2.3 端口号 VS 进程 PID:为什么不用 PID 做网络标识?
2.4 源端口与目的端口的分工差异
2.5 内核实现逻辑:端口如何定位到对应业务进程
三、Socket:网络通信的核心抽象载体
3.1 Socket 的核心定义
3.2 四元组、五元组:区分网络连接的关键标识
3.3 Linux 环境下 Socket 的内核本质
四、传输层两大核心协议:TCP 与 UDP 对比剖析
4.1 TCP 协议(传输控制协议):面向连接的可靠传输机制
4.2 UDP 协议(用户数据报协议):无连接的高效数据报传输
五、网络字节序:跨主机数据交互的统一编码规范
5.1 大端、小端存储模式原理
5.2 网络字节序的强制约束与设计初衷
5.3 字节序转换系统函数原理
函数名记忆
六、Socket 核心 API 与地址结构体深度拆解
6.1 Socket 地址结构体:C 语言实现的多态设计
6.1.1 通用地址结构体:struct sockaddr
6.1.2 IPv4 专用地址结构体:struct sockaddr_in
6.1.3 UNIX 域套接字地址结构体:struct sockaddr_un
这套设计带来两大优势
6.2 Socket 核心系统调用 API 逐行解读
6.2.1 创建 Socket 文件描述符:socket ()
6.2.2 绑定端口号与 IP 地址:bind ()
6.2.3 开启 Socket 监听:listen ()
6.2.4 接受客户端连接:accept ()
6.2.5 发起连接请求:connect ()
七、高频面试考点梳理
结束语
前言
学习 Linux 网络编程,很多人会直接上手调用 socket API 写代码,却忽略底层基础概念,只知调用接口,不懂背后内核如何完成数据收发。网络通信并不只是两台主机之间的数据传递,最终数据要交付给主机内部对应的进程进行处理。端口号如何定位进程、Socket 究竟是什么、四元组五元组起到什么作用,TCP 与 UDP 存在哪些本质差异,还有容易被忽视的网络字节序问题,都是套接字编程绕不开的前置知识。
本文将从网络通信的本质入手,一步步讲解端口号、Socket 抽象、传输层两大协议、字节序规范,再深度解析 Socket 地址结构体与核心系统调用。帮助大家打通应用层到内核的认知,搞懂每一个接口背后的原理,为后续 TCP 协议详解、服务端实战开发打下扎实基础。
一、网络通信的本质:从主机寻址到进程间的数据交付
在正式学习 Socket 之前,首先要理清一个关键问题:网络通信最终要达成的目标究竟是什么?不少人会简单认为,网络通信就是把数据从一台主机发送至另一台主机,这个理解并不完整。
- IP 地址的作用是在网络环境中唯一标识一台主机,它只能保障报文可以抵达目标机器,这仅仅是通信过程里的中间手段,并非通信的最终目的。
- 我们日常聊天、浏览网页、下载文件,本质都是应用程序之间完成数据交互。应用程序在操作系统内部以进程形态运行。也就是说,进程代表了用户在操作系统中的载体,只有数据成功交付到目标主机对应的进程,用户才可以获取数据,一次完整通信才算真正结束。
误区:报文抵达目标主机不等于通信完成,主机只是传输途中的载体。
软件层面,人通过启动各类应用进程,接收、处理网络消息。进程就是运行状态下的程序。操作系统将收到的网络数据递交给对应进程,等价于把数据交给使用软件的用户。

这里就出现了一个新问题:在系统中,同时会存在非常多的进程(浏览器、QQ、后台服务等),当数据到达目标主机之后,怎么转发给目标进程?这就要在网络的背景下,在系统中,标识主机的唯一性。 这也就是端口号要解决的核心问题,也是 Socket 编程的逻辑起点。
同时我们可以得到一个贯穿网络编程始终的核心结论:表面看网络是机器与机器的数据交互,技术本质上,实际是跨主机的进程和进程之间的通信(IPC)。
- 单机内的进程间通信,我们用管道、消息队列、共享内存;
- 跨主机的进程间通信,我们用 Socket 套接字,它把 IPC 的能力从单机扩展到了全球互联网。
二、端口号:主机内部进程的网络身份唯一标识
端口号是传输层协议的核心内容,是实现「数据精准交付给进程」的关键。
2.1 端口号的核心定义
端口号(port)是传输层协议的内容。
- 数据类型与范围:端口号是一个2 字节 16 位的无符号整数,取值范围是0 ~ 65535;
- 核心作用:是在一台主机内部对网络进程做唯一标记,告诉操作系统,收到的网络报文应当交给哪一个进程处理。
- 一个端口号只能被一个进程占用。
- 核心结论:IP 地址搭配端口号,就可以在互联网范围内,精准定位某台主机上的某一个网络进程。

2.2 端口号的范围划分
操作系统对端口号的使用做了明确的范围划分,主要分为两大类:
| 0‑1023 | 知名端口号 | 预留给主流应用层协议,普通用户进程没有权限绑定 | HTTP(80)、FTP(21)、SSH(22)、HTTPS(443) |
| 1024‑65535 | 动态分配端口号 | 操作系统自动分配,客户端临时端口取自该区间,用户程序也可自由绑定 | 浏览器、客户端 APP 临时端口,自定义服务端口 |
2.3 端口号 VS 进程 PID:为什么不用 PID 做网络标识?
不少人会产生疑问:PID 同样可以在本机唯一标识进程,为什么网络体系还要额外设计端口号?
这是网络分层设计里解耦思想的典型体现,主要有三点原因:
-
可控性不同:PID 由操作系统动态分配,进程重启之后 PID 就会改变,开发者无法人为固定。而服务端服务需要固定端口对外提供服务,例如 HTTP 默认 80 端口,客户端需要预先知晓端口才能建立通信,PID 满足不了固定标识的需求。
-
架构解耦:PID 属于进程管理模块的概念,端口号属于网络协议栈概念。倘若直接拿 PID 充当网络标识,会造成进程管理与网络模块深度耦合。一旦操作系统 PID 分配逻辑改动,网络协议栈也要跟着改动,违背分层设计原则。
-
灵活度差异:并不是全部进程都要参与网络交互。端口号允许进程自主选择是否接入网络、绑定哪个端口;一个进程也能够同时绑定多个端口,但一个端口同一时刻只能归属一个进程。这种一对多的映射能力,PID 无法实现。

2.4 源端口与目的端口的分工差异
TCP、UDP 报文头部都会携带两组端口字段:
- 源端口号:标记发送方本机发起通信的进程。
- 目的端口号:标记接收方机器上需要处理报文的目标进程。
这两个字段就是传输层实现报文分发给进程的关键。报文抵达目标主机传输层之后,内核读取目的端口号,就可以把数据递交给本机对应的进程,完成数据从主机到进程的交付。
2.5 内核实现逻辑:端口如何定位到对应业务进程
很多同学只知道端口号可以对应网络进程,却不清楚内核内部的映射实现逻辑。在 Linux 内核中依靠哈希表,完成端口到 Socket、再到进程的快速检索,核心结构体为 inet_hashinfo,定义头文件:include/net/inet_hashtables.h。
inet_hashinfo 内部维护三张不同用途的哈希表,分工处理不同网络场景:
// Linux内核 inet_hashinfo 精简示意
struct inet_hashinfo {
// 1.已连接哈希表:存放ESTABLISHED已完成连接的socket
struct inet_ehash_bucket *ehash;
// 2.绑定哈希表:以本地端口为key,管理bind绑定的socket,用于端口占用检测
struct inet_bind_hashbucket *bhash;
// 3.监听哈希表:以本机IP+端口作为key,保存LISTEN监听socket,处理客户端新建连接
struct inet_listen_hashbucket *lhash2;
};
内核报文递交给进程完整流程
三、Socket:网络通信的核心抽象载体
弄懂 IP 地址与端口号之后,就可以进一步理解 Socket 的底层本质。
3.1 Socket 的核心定义
IP 地址用来定位互联网里唯一一台主机,端口号定位这台主机内部唯一一个网络进程。把IP 地址 + 端口号组合在一起,就可以在全网范围唯一确定一个正在通信的进程,该组合就叫做套接字(Socket)。
Socket 翻译过来本意是 “插座”,这个比喻十分形象: 网络通信好比家里电器通电。IP 地址代表家庭住址,端口号就是墙上插座编号;进程相当于电器,进程绑定 Socket 就如同电器插上插座,借助电网也就是互联网,和远端设备完成数据交互。
3.2 四元组、五元组:区分网络连接的关键标识
- 四元组:{源 IP,源端口,目的 IP,目的端口},唯一标识互联网上进行交互的两个通信进程,代表通信两端端点。
- 五元组:在四元组基础上新增传输层协议(TCP/UDP)。Linux 内核依靠五元组,完整标记一条双向网络连接。
举个实例:本机 192.168.1.100 浏览器访问 CSDN 服务器 39.106.226.131:80,操作系统给浏览器分配临时端口 49152。 这条连接完整五元组:{192.168.1.100,49152,39.106.226.131,80,TCP},该五元组在全网具备唯一性。
3.3 Linux 环境下 Socket 的内核本质
Linux 秉持一切皆文件的设计理念,Socket 同样遵循这套思想。
- 用户态视角:Socket 对外表现为一个文件描述符 fd。程序直接调用 read、write 系统调用读写 fd,就可以完成网络数据收发。
- 内核态视角:Socket 是内核内部复杂结构体,保存 IP、端口、协议类型、收发缓冲区、连接状态等全套网络信息,充当用户进程和内核协议栈之间的桥梁。
四、传输层两大核心协议:TCP 与 UDP 对比剖析
4.1 TCP 协议(传输控制协议):面向连接的可靠传输机制
TCP 全称 Transmission Control Protocol,属于面向连接、可靠、面向字节流的传输层协议,主要特点如下:
面向连接:通信双方正式传输数据前,要通过三次握手完成连接建立;结束通信时执行四次挥手释放连接。类比打电话:必须拨号接通之后,双方才可以交谈。
可靠传输:依靠确认应答、超时重传、序列号、校验和整套机制,保障数据不会丢失、不会重复、按序抵达对端,规避乱序、丢包问题。
面向字节流:数据以无边界的字节流进行传输,类似自来水。发送方一次性发出 1000 字节,接收端可以分 10 次,每次读取 100 字节。发送和接收的次数没有一一对应关系,业务层需要自行划分消息边界。
4.2 UDP 协议(用户数据报协议):无连接的高效数据报传输
UDP 全称 User Datagram Protocol,属于无连接、不可靠、面向数据报的传输层协议,核心特性:
无连接 通信之前不用预先建立连接,只要知道目标 IP 与端口,就可以直接发送报文。类比寄邮件,不需要提前和收件人沟通确认,直接投递即可。
不可靠传输 UDP 没有应答确认、超时重传逻辑。只会尽力把报文发出去,既不保证对方一定收到,也不能保证报文按顺序到达。
面向数据报 传输以完整报文作为最小单位,收发次数严格对应。发送端发出一份 100 字节报文,接收端必须一次性完整读取这 100 字节,不能拆分多次读取,原生保留消息边界。
💡重要提醒: 可靠与不可靠是协议本身的属性,不等同于好坏。 TCP 实现可靠要付出开销,需要维护连接状态,因此传输开销更大,性能会有所损耗; UDP 不保障可靠,但头部短小,传输效率高、延迟更低。
- 适合 TCP:看重数据完整性,例如文件传输、转账业务、HTTP 网页通信。
- 适合 UDP:追求低延迟,可以接受少量丢包,例如直播、语音视频通话、网络游戏。

五、网络字节序:跨主机数据交互的统一编码规范
不同 CPU 架构的主机,多字节数据在内存里存储顺序不一样,也就是大小端问题。如果没有统一标准,异构主机互相通信,解析多字节数值就会错乱。网络字节序就是用来解决该问题的统一规范。
5.1 大端、小端存储模式原理
内存中存放多字节整数,相对于内存地址,分为两种存储模式:
- 小端序(Little‑Endian):低权值字节放在低内存地址,高权值字节放在高内存地址。记忆口诀:小小小(低权值字节、低内存地址->小端序)。
- 大端序(Big‑Endian):低权值字节放在高内存地址,高权值字节放在低内存地址,阅读顺序和人类习惯一致。
以 4 字节整数 0x1234abcd 举例内存布局:
| 0x0000(低地址) | 0x12 | 0xCD |
| 0x0001 | 0x23 | 0xAB |
| 0x0002 | 0xAB | 0x23 |
| 0x0003(高地址) | 0xCD | 0x12 |
简要说明:
- 大端序:高位字节(0x12)存储在低地址,低位字节(0xCD)存储在高地址。
- 小端序:低位字节(0xCD)存储在低地址,高位字节(0x12)存储在高地址。
现在 x86 架构 PC、服务器,大部分 ARM 手机、嵌入式设备,默认都是小端序;只有部分网络设备、大型机使用大端序。跨机器通信,大小端差异客观存在,必须处理。
5.2 网络字节序的强制约束与设计初衷
TCP/IP 协议硬性规定:网络数据流一律使用大端字节序(低地址存放高字节)。
为什么网络选用大端序?
网络发送规则:内存低地址的数据优先向外发送。大端模式下最先发出的是数值高字节,抓包观察报文时,阅读逻辑更贴合人类习惯,可读性更好。
5.3 字节序转换系统函数原理
为实现代码跨平台可移植,保证同一份 C 代码在大端和小端主机上都能正常运行,Linux 提供一套标准字节序转换接口,头文件:<arpa/inet.h>
函数原型与核心说明:
#include <arpa/inet.h>
// 主机序 → 网络序,32位长整数,多用于IP
uint32_t htonl(uint32_t hostlong);
// 主机序 → 网络序,16位短整数,多用于端口号
uint16_t htons(uint16_t hostshort);
// 网络序 → 主机序,32位长整数
uint32_t ntohl(uint32_t netlong);
// 网络序 → 主机序,16位短整数
uint16_t ntohs(uint16_t netshort);
函数名记忆
- h = host,主机字节序
- n = network,网络字节序
- l = long,32 位无符号整数
- s = short,16 位无符号整数
举例:htons → host‑to‑network‑short,把 16 位端口从主机序转为网络字节序。

函数底层逻辑
这一组函数会自适应本机大小端:
- 如果本机是小端序:内部执行字节翻转,完成大小端转换;
- 如果本机是大端序:不需要改动,参数直接原样返回。
开发规范:写网络代码,无论你清楚当前机器是大端还是小端,都要调用转换函数。不需要手动检测本机字节序,保证一份代码可以在不同架构机器正常编译运行。
⚠️重要提示: 单字节字符串不需要字节序转换。大小端只作用于 short、int、long 这类占用多个字节的数值类型;单个字节不存在存储顺序问题,而发送字符串时,是一个字母一个字母独立发送的(单字节),接收也是单字节接收。
六、Socket 核心 API 与地址结构体深度拆解
6.1 Socket 地址结构体:C 语言实现的多态设计
Socket API 属于一套抽象通用接口,除 IPv4 之外,还支持 IPv6、UNIX 域套接字等多种地址格式。为实现同一套接口适配各类地址,系统在 C 语言层面实现多态思想,主要依靠三类地址结构体完成。
6.1.1 通用地址结构体:struct sockaddr
// 通用地址结构体,所有Socket API的参数都用这个类型
struct sockaddr
{
sa_family_t sa_family; // 16位地址类型,标识是哪种地址协议
char sa_data[14];// 14字节地址数据,不同协议有不同的格式
};
- sa_family:地址族,标记地址协议类型,常用取值:
- AF_INET:代表 IPv4 协议
- AF_UNIX/AF_LOCAL:UNIX 域套接字,用于本机进程间通信
6.1.2 IPv4 专用地址结构体:struct sockaddr_in
头文件:<netinet/in.h>,专门用于 IPv4 网络通信,内存大小和 sockaddr 保持一致。
#include <netinet/in.h>
// IPv4地址结构体,和sockaddr长度完全一致
struct sockaddr_in
{
sa_family_t sin_family; // 地址类型,必须填 AF_INET
in_port_t sin_port; // 16位端口号,**必须使用网络字节序**
struct in_addr sin_addr; // 32位IP地址,**必须使用网络字节序**
unsigned char sin_zero[8]; // 8字节填充位,保证结构体大小与sockaddr对齐
};
6.1.3 UNIX 域套接字地址结构体:struct sockaddr_un
头文件:<sys/un.h>,用于同一台机器上进程之间本地通信,不经过网络协议栈。
#include <sys/un.h>
// UNIX域套接字地址结构体,用于本地进程间通信
struct sockaddr_un
{
sa_family_t sun_family; // 地址类型,必须填 AF_UNIX
char sun_path[108]; // 本地socket文件路径名
};


设计精髓:C 语言模拟多态的原理
Socket 全部系统调用接收的参数类型都是 struct sockaddr *。 开发时,我们根据通信协议选择对应的专用结构体,例如 sockaddr_in,把 IP、端口等字段填充完毕后,强制转换成 struct sockaddr * 传入函数。 内核读取结构体首个成员 sa_family,以此判断真实地址类型,再解析后面的内存数据。 本质就是基类指针接收派生类对象,在没有面向对象语法的 C 语言中,模拟实现多态效果。
这套设计带来两大优势
6.2 Socket 核心系统调用 API 逐行解读
Socket 编程分为服务端与客户端两大角色,核心共有 5 个系统 API,下面逐个拆解函数原型、参数、返回值与代码示例。
6.2.1 创建 Socket 文件描述符:socket ()
函数原型:
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:生成 socket 文件描述符,在内核中创建 socket 内核对象,为后续网络通信做好初始化准备。TCP、UDP,不管是服务端还是客户端,第一步都要调用该函数。
参数解析
- domain:地址族 / 协议族,选定底层通信协议
- AF_INET:IPv4 协议,最常用
- AF_INET6:IPv6 协议
- AF_UNIX / AF_LOCAL:UNIX 域套接字,本机进程间通信
- type:套接字类型,确定传输特性
- SOCK_STREAM:流式套接字,对应 TCP,可靠、面向连接、字节流传输
- SOCK_DGRAM:数据报套接字,对应 UDP,无连接、不可靠报文传输
- protocol:子协议编号,一般填0,系统自动根据 domain 和 type 匹配对应协议。
返回值
- 成功:返回非负整数,socket 文件描述符
- 失败:返回-1,全局变量errno记录错误原因
使用示例:
// 创建一个TCP Socket,基于IPv4协议
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket create failed");
exit(1);
}
6.2.2 绑定端口号与 IP 地址:bind ()
函数原型:
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
功能:把 socket 文件描述符和指定 IP、端口进行绑定。服务端必须调用,客户端一般不需要手动 bind。
参数解析
- socket:socket () 返回的文件描述符
- address:填充完毕的地址结构体指针,需要强制转为struct sockaddr *通用类型
- address_len:地址结构体字节大小,例如sizeof(struct sockaddr_in)
返回值:
- 成功:返回0
- 失败:返回-1,设置 errno;常见错误:端口被占用、权限不足(绑定 1023 以下端口需要 root 权限)
使用示例:
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET; // IPv4协议
serv_addr.sin_port = htons(8080); // 端口号,主机转网络序
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定本机所有网卡IP
// 将Socket与地址绑定
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
perror("bind failed");
close(sockfd);
}
关键说明:INADDR_ANY代表监听本机全部网卡 IP,外部客户端访问本机任意 IP 的该端口,都可以被 socket 接收,是服务端最常用配置。
6.2.3 开启 Socket 监听:listen ()
函数原型:
int listen(int socket, int backlog);
功能:把 socket 切换为被动监听模式,内核开始接收客户端连接请求。只用于 TCP 服务端;UDP、客户端不需要调用 listen。
参数解析:
- socket:经过 bind 绑定地址后的 socket 文件描述符
- backlog:已完成三次握手的连接队列最大长度,代表同一时刻最多多少个已经建立完成、等待 accept 处理的连接。
返回值:
- 成功:返回0
- 失败:返回-1,设置 errno
使用示例:
// 开启监听,backlog设置为128
if (listen(sockfd, 128) < 0)
{
perror("listen failed");
close(sockfd);
exit(1);
}
6.2.4 接受客户端连接:accept ()
函数原型:
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
功能:从内核已完成三次握手的连接队列取出一条连接,生成全新 socket 文件描述符专门和该客户端通信。仅 TCP 服务端使用。
参数解析:
- socket:listen 使用的监听 socket fd
- address:输出参数,用来存放对端客户端 IP 与端口信息
- address_len:输入输出参数;传入是地址结构体大小;函数返回后,里面是实际拷贝的地址长度
返回值:
- 成功:返回全新非负 socket fd,专门用来和当前客户端收发数据
- 失败:返回-1,设置 errno
使用示例:
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
// 阻塞等待客户端连接
int client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0)
{
perror("accept failed");
close(sockfd);
exit(1);
}
核心要点:accept 返回的新 fd 才是和客户端通信的 socket;原始监听 socket 会继续保持监听,持续等待新客户端接入。
6.2.5 发起连接请求:connect ()
函数原型:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:TCP 客户端向服务端发起连接请求,完成三次握手建立连接。只由 TCP 客户端调用。
参数解析:
- sockfd:客户端 socket 函数创建出的文件描述符
- addr:服务端地址结构体,填充服务端 IP、端口
- addrlen:地址结构体所占字节大小
返回值:
- 成功:返回0,连接建立完成,可以读写 sockfd 收发数据
- 失败:返回-1,设置 errno;常见:服务端不可达、端口未开放、连接超时
使用示例:
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
// 填充服务端IP地址
inet_pton(AF_INET, "192.168.1.100", &serv_addr.sin_addr);
// 向服务端发起连接
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
perror("connect failed");
}
七、高频面试考点梳理
-
网络通信的本质
跨主机之间的进程间通信。数据包到达目标主机只是中间步骤,最终目标是将数据交给主机上对应的业务进程。
-
端口号的核心作用
16 位无符号整数,用来区分同一主机内不同网络进程。IP+端口 可以在全网唯一定位一个网络进程。
-
端口号与 PID 的区别,为什么不用 PID 做网络标识
PID 进程 ID 会随进程启停不断变化,不能固定;端口可以固定绑定服务,实现网络层和进程 ID 解耦,部署更灵活可控,因此不直接使用 PID。
-
Socket 的本质
逻辑上 IP+端口 代表一个 socket 端点;Linux 实现层面,socket 本质是文件描述符,充当用户进程和内核协议栈交互的桥梁。
-
四元组与五元组
四元组:包含源 IP、源端口、目的 IP、目的端口,标识通信两端; 五元组:额外增加协议类型 (TCP/UDP),能够唯一标识一条网络连接。
-
TCP 与 UDP 的核心特性以及适用场景
TCP:面向连接、可靠传输、字节流;适合文件传输、网页等对数据可靠要求高的场景。 UDP:无连接、不可靠、数据报;适合直播、DNS、游戏等追求低延迟,可容忍少量丢包场景。
-
网络字节序
TCP/IP 协议统一采用大端序作为网络字节序。主机存在大小端差异,发送数据前需要将主机序转为网络大端序,接收之后再转回主机序;主要转换函数:htons、htonl、ntohs、ntohl。
-
Socket 地址结构体的设计思想
借助通用基结构体struct sockaddr,利用 C 语言强制类型转换模拟多态,同一套系统调用接口,就可以兼容 IPv4、IPv6、UNIX 域套接字多种地址类型。
-
Socket 核心 API 的作用与使用场景
- socket:创建 socket 文件描述符,客户端、服务端都要调用
- bind:绑定 IP 和端口,主要服务端使用
- listen:开启监听,仅 TCP 服务端调用
- accept:获取已建立连接,仅 TCP 服务端调用,返回新 fd 用于通信
- connect:发起三次握手建立连接,仅 TCP 客户端调用
-
内核如何通过端口号找到进程
内核依靠inet_hashinfo维护三张哈希表存储 socket 相关信息;网络报文抵达主机后,内核根据报文中 IP、端口查询哈希表,定位对应的 socket 对象,最终将数据交付给用户进程。
结束语
本篇我们从网络通信的本质出发,依次讲解了端口号的作用,厘清了端口与 PID 的区别,理解了内核依靠端口把报文交付给对应进程的底层逻辑。接着认识了 Socket 这一网络编程抽象,掌握四元组、五元组如何唯一标识一条网络连接,剖析了 Linux 下 Socket 在内核中的真实形态。随后对比了传输层 TCP、UDP 两大协议的核心特性,解释了网络字节序存在的意义,搞懂大小端差异以及字节序转换函数的用法,最后对 Socket 地址结构体和核心系统调用做了完整解析。
端口、Socket、字节序、系统调用,是 Linux 网络编程的地基,很多后续 TCP 连接、IO 模型、服务端开发的内容都建立在这些知识之上。把这部分基础吃透,后续学习套接字编程、手写服务端代码时,才不会只停留在调用 API 的表层,能够看懂背后内核的行为。





