API详情
一、核心 Socket API 详解
1.1 socket() – 创建套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参数
domain:协议族 / 地址族,常用:AF_INET:IPv4AF_INET6:IPv6AF_UNIX/AF_LOCAL:本地 Unix 域套接字
type:套接字类型,常用:SOCK_STREAM:TCP 流式套接字SOCK_DGRAM:UDP 数据报套接字
protocol:通常设为 0,表示自动选择type对应的默认协议(TCP/UDP)。- 也可以显式指定
IPPROTO_TCP、IPPROTO_UDP等。
- 也可以显式指定
返回值
- 成功:返回套接字文件描述符(非负整数)。
- 失败:返回 -1,并设置
errno。
典型用法
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
1.2 bind() – 绑定地址和端口
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数
sockfd:socket()返回的套接字描述符。addr:指向地址结构的指针,通常使用struct sockaddr_in(IPv4)或struct sockaddr_in6(IPv6),强制转换为struct sockaddr*。addrlen:地址结构长度,通常用sizeof(struct sockaddr_in)等。
典型用法(IPv4 服务器)
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 0.0.0.0,本机所有 IP
servaddr.sin_port = htons(8080);
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
perror("bind");
close(listenfd);
exit(EXIT_FAILURE);
}
说明
- 服务器通常需要
bind()固定端口;客户端一般不显式bind,由系统自动分配临时端口。 INADDR_ANY表示绑定本机所有 IP,多网卡场景常用。
1.3 listen() – 将套接字设为监听状态(TCP 服务器)
int listen(int sockfd, int backlog);
参数
sockfd:套接字描述符,一般先socket()+bind()。backlog:内核为此套接字维护的两个队列之和的建议最大值:- 未完成连接队列(SYN_RCVD 状态)
- 已完成连接队列(ESTABLISHED 状态)
返回值
- 成功:0。
- 失败:-1,设置
errno。
说明
listen()将套接字从「主动套接字」变为「被动套接字」,用于接受连接。- 实际可排队连接数受内核参数
/proc/sys/net/core/somaxconn限制。
1.4 accept() – 接受新连接(TCP 服务器)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数
sockfd:监听套接字(listen()之后的那个)。addr:用来返回客户端地址的结构体指针,可为NULL。addrlen:值-结果参数,调用前是addr缓冲区长度,返回时为实际地址长度。
返回值
- 成功:返回一个新的已连接套接字描述符,用于与该客户端通信。
- 失败:返回 -1,设置
errno。
典型用法
struct sockaddr_in cliaddr;
socklen_t clilen = sizeof(cliaddr);
int connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
if (connfd == -1) {
perror("accept");
// 视情况决定是否退出
}
要点
listenfd是监听套接字,生命周期伴随整个服务器;connfd是已连接套接字,每个客户端一个。accept()默认阻塞,直到有新连接到达。
1.5 connect() – 客户端建立连接(TCP)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数
sockfd:客户端套接字(一般socket()得到,未bind)。addr:服务器地址结构。addrlen:地址长度。
返回值
- 成功:0(连接建立或超时/被拒绝)。
- 失败:-1,设置
errno。
典型用法
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("192.168.1.100");
servaddr.sin_port = htons(8080);
if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
perror("connect");
close(sockfd);
exit(EXIT_FAILURE);
}
1.6 close() – 关闭套接字
int close(int fd);
参数
fd:要关闭的文件描述符(包括套接字)。
返回值
- 成功:0。
- 失败:-1。
说明
- 对于 TCP,
close()会触发四次挥手;但实际发送 FIN 等行为还受SO_LINGER等选项影响。 - 套接字也是文件描述符,用完后必须关闭,避免 fd 耗尽。
1.7 shutdown() – 优雅关闭连接
int shutdown(int sockfd, int how);
参数
sockfd:套接字描述符。how:SHUT_RD:关闭读端,不再接收数据。SHUT_WR:关闭写端,不再发送数据,发送 FIN。SHUT_RDWR:同时关闭读写。
返回值
- 成功:0。
- 失败:-1。
说明
shutdown()可以只关闭一个方向,实现「半关闭」。- 即使引用计数不为 0(多进程/多线程共享),
shutdown()也会直接影响连接状态。
二、I/O 相关 API:read/write/recv/send/recvfrom/sendto
2.1 read() / write() – 通用文件 I/O
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
参数
fd:文件描述符(套接字也用这两个函数)。buf:缓冲区。count:要读写的字节数。
返回值
- 成功:
read:返回实际读到的字节数,0 表示 EOF(对端关闭)。write:返回实际写入的字节数。
- 失败:返回 -1,设置
errno。
说明
- 对套接字而言,
read/write是最通用的 I/O 方式。 - 对端正常关闭时,
read返回 0;异常则返回 -1。
2.2 recv() / send() – 套接字专用 I/O
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数
sockfd:套接字描述符。buf:数据缓冲区。len:缓冲区长度。flags:标志位,常用:MSG_DONTWAIT:非阻塞操作(相当于临时非阻塞)。MSG_OOB:发送/接收带外数据。MSG_PEEK:查看数据但不从缓冲区移除。MSG_WAITALL:请求读取完整数据报或指定长度(阻塞直到满足或出错)。
返回值
- 成功:返回实际读/写的字节数。
- 失败:返回 -1,设置
errno。 - 对端正常关闭:
recv返回 0。
说明
send/recv比write/read多了一个flags参数,可以控制行为。send只是把数据从用户态拷贝到内核发送缓冲区,真正发送由协议栈完成。
2.3 recvfrom() / sendto() – 支持 UDP 的读写
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
参数
- 前 4 个参数与
send/recv相同。 dest_addr/src_addr:对端地址结构,用于 UDP 指定/获取对端地址。addrlen:地址长度。
说明
- 对于 UDP,通常使用
sendto/recvfrom来指定或获取对方地址。 - TCP 中也可以使用,但一般直接用
send/recv即可。
2.4 recvmsg() / sendmsg() – 更复杂的 I/O
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
参数
msg:struct msghdr结构,包含:- 多个缓冲区(
msg_iov/msg_iovlen)。 - 可选的控制信息(
msg_control/msg_controllen)。 - 对端地址信息。
- 多个缓冲区(
说明
- 适合需要「多缓冲区」或「辅助数据」的场景,例如:
- 传递文件描述符(Unix 域套接字 +
SCM_RIGHTS)。 - 获取/设置 IP 层信息(如 IP_TTL、IP_TOS)。
- 传递文件描述符(Unix 域套接字 +
三、I/O 多路复用:select / poll / epoll
3.1 select() – 传统多路复用
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数
nfds:集合中最大文件描述符 + 1。readfds:监视可读的 fd 集合。writefds:监视可写的 fd 集合。exceptfds:监视异常的 fd 集合。timeout:超时时间,NULL表示永久阻塞。
辅助宏
FD_ZERO(fd_set *set); // 清空集合
FD_SET(int fd, fd_set *set); // 将 fd 加入集合
FD_CLR(int fd, fd_set *set); // 将 fd 从集合中移除
FD_ISSET(int fd, fd_set *set); // 判断 fd 是否在集合中
返回值
- 成功:返回就绪 fd 的总数(包括读写异常)。
- 超时:返回 0。
- 失败:返回 -1,设置
errno。
缺点
- 每次调用都要线性扫描整个 fd 集合。
- 单个进程能够监视的 fd 数量受
FD_SETSIZE限制(一般 1024)。
3.2 poll() – 更灵活的多路复用
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数
fds:pollfd数组:struct pollfd { int fd; // 文件描述符 short events; // 监视的事件(如 POLLIN | POLLOUT) short revents; // 返回的事件(由内核填充) };nfds:数组元素个数。timeout:超时时间(毫秒),-1 表示永久阻塞。
返回值
- 成功:就绪 fd 的数量。
- 超时:0。
- 失败:-1。
优点
- 没有
FD_SETSIZE的硬编码限制。 - 接口比
select更直观,易于扩展。
3.3 epoll – Linux 高性能多路复用
3.3.1 epoll_create() – 创建 epoll 实例
int epoll_create(int size);
参数
size:只是建议值,Linux 2.6.8 之后被忽略,只需大于 0 即可。
返回值
- 成功:返回 epoll 实例的文件描述符。
- 失败:-1。
3.3.2 epoll_ctl() – 注册/修改/删除事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数
epfd:epoll_create返回的 epoll fd。op:操作类型:EPOLL_CTL_ADD:添加 fd 到 epoll 实例。EPOLL_CTL_MOD:修改已注册 fd 的事件。EPOLL_CTL_DEL:删除 fd。
fd:要操作的文件描述符。event:struct epoll_event指针:struct epoll_event { uint32_t events; /* Epoll events (EPOLLIN, EPOLLOUT, EPOLLET, ...) */ epoll_data_t data; /* User data variable */ }; typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t;
返回值
- 成功:0。
- 失败:-1。
3.3.3 epoll_wait() – 等待事件发生
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
参数
epfd:epoll 实例 fd。events:数组,用来返回就绪事件。maxevents:数组大小。timeout:超时(毫秒),-1 表示阻塞。
返回值
- 成功:就绪 fd 的数量。
- 超时:0。
- 失败:-1。
说明
epoll使用事件驱动模型,只返回活跃的 fd,适合高并发场景。- 支持 LT(水平触发)和 ET(边缘触发)两种模式,ET 需配合非阻塞套接字。
四、地址转换与解析 API
4.1 inet_pton() / inet_ntop() – IP 地址二进制与文本转换
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数
af:地址族,AF_INET或AF_INET6。src:inet_pton:点分十进制 IPv4 字符串(如 “192.168.1.1”)或 IPv6 文本形式。inet_ntop:网络字节序的二进制 IP 地址。
dst:inet_pton:存放转换后的网络字节序地址(struct in_addr/struct in6_addr)。inet_ntop:用于存放结果字符串的缓冲区。
size:缓冲区大小(inet_ntop)。
返回值
inet_pton:- 成功:1。
- 格式无效:0。
- 失败:-1。
inet_ntop:- 成功:返回
dst指针。 - 失败:返回
NULL。
- 成功:返回
说明
- 推荐用这两个函数代替旧的
inet_addr、inet_aton、inet_ntoa,它们支持 IPv4 和 IPv6。
4.2 getaddrinfo() / freeaddrinfo() – 现代地址解析
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
void freeaddrinfo(struct addrinfo *res);
参数
node:主机名或 IP 字符串(如 “www.example.com” 或 “192.168.1.1”)。service:服务名或端口号字符串(如 “http” 或 “80”)。hints:addrinfo结构,指定过滤条件:struct addrinfo { int ai_flags; // AI_PASSIVE, AI_CANONNAME 等 int ai_family; // AF_INET, AF_INET6, AF_UNSPEC int ai_socktype; // SOCK_STREAM, SOCK_DGRAM int ai_protocol; // 0 或 IPPROTO_TCP/UDP size_t ai_addrlen; // 地址长度 struct sockaddr *ai_addr; // 地址结构 char *ai_canonname; // 正式主机名 struct addrinfo *ai_next; // 链表下一节点 };res:返回的链表头指针,每个节点包含一个可用地址。
返回值
- 成功:0。
- 失败:返回非零错误码,可用
gai_strerror()转为错误信息。
说明
getaddrinfo同时处理:- 主机名到 IP 地址的解析(DNS)。
- 服务名到端口的映射。
- 返回可直接用于
socket/bind/connect的地址结构。
- 支持 IPv4/IPv6 双栈编程,是现代网络编程推荐方式。
4.3 getnameinfo() – 地址到主机名/服务名的逆向解析
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen,
int flags);
参数
addr/addrlen:地址结构及长度。host/hostlen:存放主机名缓冲区。serv/servlen:存放服务名(端口号)缓冲区。flags:如NI_NUMERICHOST、NI_NUMERICSERV等,控制是否返回数字形式。
说明
- 相当于「反向 DNS + 端口解析」,是
gethostbyaddr/getservbyport的现代替代。
五、套接字选项与辅助 API
5.1 setsockopt() / getsockopt() – 设置/获取套接字选项
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
参数
sockfd:套接字描述符。level:选项级别:SOL_SOCKET:套接字层选项。IPPROTO_TCP:TCP 选项。IPPROTO_IP:IP 选项。IPPROTO_IPV6:IPv6 选项。
optname:选项名,如:SO_REUSEADDR:地址重用。SO_KEEPALIVE:保活探测。SO_BROADCAST:广播。SO_RCVBUF/SO_SNDBUF:收发缓冲区大小。SO_RCVTIMEO/SO_SNDTIMEO:收发超时。TCP_NODELAY:禁用 Nagle 算法。
optval:选项值缓冲区。optlen:缓冲区长度。
典型用法
int reuse = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
int keepalive = 1;
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
5.2 fcntl() – 设置非阻塞/文件描述符标志
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
常用命令
F_GETFL:获取文件状态标志。F_SETFL:设置文件状态标志,常用:O_NONBLOCK:非阻塞 I/O。
示例:设置套接字为非阻塞
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
5.3 getsockname() / getpeername() – 获取本地/对端地址
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数
sockfd:套接字描述符。addr/addrlen:返回地址结构。
说明getsockname:返回本地协议地址(IP + 端口)。getpeername:返回对端协议地址。
六、分门别类归纳表
下列部分是对上述内容的总结归纳
6.1 套接字创建与连接
| 类别 | API | 作用简述 | 典型使用场景 |
|---|---|---|---|
| 创建套接字 | socket() | 创建套接字,返回文件描述符 | TCP/UDP 服务器/客户端第一步 |
| 绑定地址 | bind() | 绑定 IP 地址和端口到套接字 | 服务器固定端口;客户端一般不调用 |
| 监听连接 | listen() | 将套接字设为监听状态,维护连接队列 | TCP 服务器在 bind 之后调用 |
| 接受连接 | accept() | 从监听队列取出一个已完成连接,返回新套接字 | TCP 服务器循环调用,为每个客户端服务 |
| 发起连接 | connect() | 客户端向服务器发起连接(TCP) | TCP 客户端建立连接 |
| 关闭套接字 | close() | 关闭套接字,释放资源 | 所有不再使用的套接字都需要关闭 |
| 优雅关闭 | shutdown() | 关闭读/写端,支持半关闭 | 需要单独关闭读或写方向时使用 |
6.2 I/O 读写
| 类别 | API | 作用简述 | 典型使用场景 |
|---|---|---|---|
| 通用 I/O | read() | 从文件描述符读取数据 | 对套接字也适用,简单读取 |
| 通用 I/O | write() | 向文件描述符写入数据 | 对套接字也适用,简单发送 |
| 套接字 | recv() | 接收数据,支持 flags | TCP 常用,可带 MSG_PEEK 等 |
| 套接字 | send() | 发送数据,支持 flags | TCP 常用,可带 MSG_DONTWAIT 等 |
| UDP | recvfrom() | 接收数据,同时获取对端地址 | UDP 服务器/客户端常用 |
| UDP | sendto() | 发送数据,指定目标地址 | UDP 发送数据到指定目标 |
| 高级 I/O | recvmsg() | 支持 msghdr 的复杂接收 | 多缓冲区、控制信息、传递 fd 等 |
| 高级 I/O | sendmsg() | 支持 msghdr 的复杂发送 | 多缓冲区、控制信息、传递 fd 等 |
6.3 I/O 多路复用
| 类别 | API | 作用简述 | 典型使用场景 |
|---|---|---|---|
| 传统 | select() | 多路复用,监视多个 fd 的读写异常 | 简单服务器,兼容性要求高 |
| 传统 | poll() | 类似 select,但无 FD_SETSIZE 限制 | 中等规模并发服务器 |
| Linux 高性能 | epoll_create() | 创建 epoll 实例 | 高并发服务器(如 Web、游戏服务器) |
| Linux 高性能 | epoll_ctl() | 添加/修改/删除 epoll 事件 | 在主循环中维护关注的 fd |
| Linux 高性能 | epoll_wait() | 等待就绪事件 | 事件驱动主循环 |
6.4 地址转换与解析
| 类别 | API | 作用简述 | 典型使用场景 |
|---|---|---|---|
| IP 转换 | inet_pton() | 文本 IP → 网络字节序二进制 | 解析命令行或配置文件中的 IP 地址 |
| IP 转换 | inet_ntop() | 网络字节序二进制 → 文本 IP | 打印日志、调试、显示客户端 IP |
| 地址解析 | getaddrinfo() | 解析主机名 + 服务名为 addrinfo 链表 | 现代 TCP/UDP 客户端/服务器初始化 |
| 地址解析 | freeaddrinfo() | 释放 getaddrinfo 返回的链表 | 与 getaddrinfo 配对使用 |
| 逆向解析 | getnameinfo() | 地址结构 → 主机名 + 服务名 | 日志中记录主机名而不仅是 IP |
6.5 套接字选项与辅助 API
| 类别 | API | 作用简述 | 典型使用场景 |
|---|---|---|---|
| 选项设置 | setsockopt() | 设置套接字选项(缓冲区、重用、保活等) | 端口重用、开启 KEEPALIVE、设置超时等 |
| 选项获取 | getsockopt() | 获取套接字选项当前值 | 调试、查看默认缓冲区大小等 |
| 非阻塞 | fcntl() | 设置/获取文件描述符标志,常用 O_NONBLOCK | 将套接字设置为非阻塞模式,配合 epoll/ET |
| 本地地址 | getsockname() | 获取套接字本地地址(IP + 端口) | 查看系统自动分配的临时端口等 |
| 对端地址 | getpeername() | 获取套接字对端地址 | 已连接套接字需要记录客户端 IP/端口时 |
相关结构体详情
一、地址相关的结构体
1.1 通用地址结构 sockaddr
struct sockaddr {
sa_family_t sa_family; // 地址族:AF_INET、AF_INET6、AF_UNIX 等
char sa_data[14]; // 协议地址(具体含义由协议决定)
};
- 作用:
bind/connect/sendto/recvfrom等函数的「通用地址参数」,实际使用时会根据sa_family转成具体的sockaddr_in/sockaddr_in6/sockaddr_un等类型。
1.2 IPv4 套接字地址 sockaddr_in / in_addr
struct sockaddr_in {
sa_family_t sin_family; // AF_INET
in_port_t sin_port; // 端口,网络字节序
struct in_addr sin_addr; // IPv4 地址
unsigned char sin_zero[8]; // 填充,使大小与 sockaddr 一致
};
struct in_addr {
in_addr_t s_addr; // 32bit IPv4 地址,网络字节序
};
- 使用场景:IPv4 TCP/UDP 服务器/客户端
bind/connect时设置本地/对端地址。 - 注意:
sin_port必须用htons()转为网络字节序。sin_addr.s_addr可设为INADDR_ANY(0.0.0.0)表示本机任意地址。
1.3 IPv6 套接字地址 sockaddr_in6 / in6_addr
struct sockaddr_in6 {
sa_family_t sin6_family; // AF_INET6
in_port_t sin6_port; // 端口号,网络字节序
uint32_t sin6_flowinfo; // IPv6 流信息
struct in6_addr sin6_addr; // IPv6 地址
uint32_t sin6_scope_id; // 作用域 ID(link-local 时为接口索引)
};
struct in6_addr {
uint8_t s6_addr[16]; // 128bit IPv6 地址
};
- 使用场景:IPv6 套接字绑定地址、连接对端。
1.4 Unix 域套接字地址 sockaddr_un
struct sockaddr_un {
sa_family_t sun_family; // AF_UNIX / AF_LOCAL
char sun_path[108]; // 路径名(或抽象地址)
};
- 使用场景:本地进程间通信(本地套接字)。
sun_path可以是:- 文件系统中的路径(
bind后会创建一个套接字文件)。 - 抽象地址(Linux 特有,
sun_path[0] == '\0')。
- 文件系统中的路径(
二、I/O 多路复用相关结构体
2.1 pollfd – poll 专用
struct pollfd {
int fd; // 监视的文件描述符
short events; // 用户关心的事件(POLLIN、POLLOUT 等)
short revents; // 实际发生的事件,由内核填充
};
- 使用场景:
poll()的参数,每个 fd 对应一个pollfd。
2.2 epoll_event / epoll_data – epoll 专用
struct epoll_event {
uint32_t events; // 事件掩码:EPOLLIN、EPOLLOUT、EPOLLET 等
epoll_data_t data; // 用户数据,可存 fd 或指针
};
union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
};
typedef union epoll_data epoll_data_t;
- 使用场景:
epoll_ctl/epoll_wait,data常用来保存「连接上下文」指针或 fd。
三、高级 I/O 相关结构体
3.1 iovec – 分散/聚集 I/O 向量
struct iovec {
void *iov_base; // 缓冲区起始地址
size_t iov_len; // 缓冲区长度
};
- 使用场景:
readv/writev一次操作多个缓冲区。sendmsg/recvmsg中通过msg_iov数组指定多段缓冲区。
3.2 msghdr / cmsghdr – sendmsg/recvmsg 消息头
struct msghdr {
void *msg_name; // 可选的对端地址(未连接套接字)
socklen_t msg_namelen; // 地址长度
struct iovec *msg_iov; // scatter/gather 缓冲区数组
size_t msg_iovlen; // msg_iov 中元素个数
void *msg_control; // 辅助数据(控制信息)缓冲区
size_t msg_controllen; // 辅助数据缓冲区长度
int msg_flags; // 接收消息的标志(recvmsg 返回)
};
struct cmsghdr {
socklen_t cmsg_len; // 总长度(包含头部)
int cmsg_level; // 协议层(SOL_SOCKET / IPPROTO_IP / IPPROTO_IPV6)
int cmsg_type; // 控制消息类型
/* 后面是实际数据 unsigned char cmsg_data[]; */
};
- 使用场景:
sendmsg/recvmsg实现带外数据、传递文件描述符(Unix 域套接字)、IP 选项等高级功能。- 通过
CMSG_FIRSTHDR/CMSG_NXTHDR/CMSG_DATA等宏访问控制信息。
四、名字解析相关结构体
4.1 addrinfo – getaddrinfo 返回的地址链表
struct addrinfo {
int ai_flags; // AI_PASSIVE、AI_CANONNAME 等
int ai_family; // AF_INET、AF_INET6、AF_UNSPEC
int ai_socktype; // SOCK_STREAM、SOCK_DGRAM
int ai_protocol; // IPPROTO_TCP、IPPROTO_UDP 或 0
size_t ai_addrlen; // ai_addr 的长度
struct sockaddr *ai_addr; // 指向地址结构(sockaddr_in / sockaddr_in6)
char *ai_canonname; // 正式主机名(若 AI_CANONNAME)
struct addrinfo *ai_next; // 链表下一个节点
};
- 使用场景:
getaddrinfo()的结果,用于替代gethostbyname,实现协议无关的地址解析。
4.2 hostent – 传统主机地址信息(已废弃)
struct hostent {
char *h_name; // 主机正式名称
char **h_aliases; // 别名列表(NULL 结尾)
int h_addrtype; // 地址类型:AF_INET 或 AF_INET6
int h_length; // 地址长度(字节)
char **h_addr_list; // 网络地址列表(网络字节序),NULL 结尾
};
#define h_addr h_addr_list[0] // 向后兼容宏
- 使用场景:
gethostbyname/gethostbyaddr的返回值;现代代码建议用getaddrinfo/getnameinfo。
4.3 servent – 服务数据库信息
struct servent {
char *s_name; // 服务的正式名称
char **s_aliases; // 别名列表(NULL 结尾)
int s_port; // 端口号,网络字节序
char *s_proto; // 协议名,例如 "tcp" / "udp"
};
- 使用场景:
getservbyname/getservbyport查询/etc/services中的服务信息。
五、选项与时间相关结构体
5.1 linger – SO_LINGER 选项
struct linger {
int l_onoff; // 是否启用 LINGER:0 关闭,非 0 启用
int l_linger; // 启用时,close() 等待的秒数
};
- 使用场景:
setsockopt设置SO_LINGER,控制close行为:l_onoff=0:默认,close立即返回,内核尽量优雅关闭。l_onoff!=0 && l_linger=0:close立即返回,丢弃发送缓冲区中数据,发 RST 强制关闭。l_onoff!=0 && l_linger>0:close最多等待l_linger秒,未发完数据则返回错误。
5.2 timeval – 时间间隔/时间戳
struct timeval {
time_t tv_sec; // 秒
suseconds_t tv_usec; // 微秒
};
- 使用场景:
gettimeofday获取当前时间。setsockopt设置SO_RCVTIMEO/SNDTIMEO作为超时参数。
六、网络接口相关结构体(ioctl 操作网卡)
6.1 ifreq – 单个网络接口配置
struct ifreq {
char ifr_name[IFNAMSIZ]; // 接口名,例如 "eth0"
union {
struct sockaddr ifru_addr; // 地址
struct sockaddr ifru_dstaddr; // 点对点目的地址
struct sockaddr ifru_broadaddr; // 广播地址
short ifru_flags; // 标志(IFF_UP 等)
int ifru_metric; // 路由度量
caddr_t ifru_data; // 其他数据
} ifr_ifru;
};
// 常用宏
#define ifr_addr ifr_ifru.ifru_addr
#define ifr_dstaddr ifr_ifru.ifru_dstaddr
#define ifr_broadaddr ifr_ifru.ifru_broadaddr
#define ifr_flags ifr_ifru.ifru_flags
#define ifr_metric ifr_ifru.ifru_metric
#define ifr_data ifr_ifru.ifru_data
- 使用场景:
ioctl(SIOCGIFADDR)/SIOCGIFFLAGS等获取/设置 IP、掩码、标志、MTU 等。
6.2 ifconf – 所有网络接口列表
struct ifconf {
int ifc_len; // 缓冲区长度
union {
char *ifcu_buf; // 缓冲区地址
struct ifreq *ifcu_req; // ifreq 数组
} ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf
#define ifc_req ifc_ifcu.ifcu_req
- 使用场景:
ioctl(SIOCGIFCONF)一次性获取所有网卡信息,结果是一个ifreq数组。
七、分类归纳表
下面按用途把结构体分成几类:
| 类别 | 结构体 | 主要用途简述 | 典型使用场景 |
|---|---|---|---|
| 通用地址 | sockaddr | 通用地址结构,传给很多 socket API | bind/connect/sendto 等的地址参数 |
| IPv4 地址 | sockaddr_in | IPv4 套接字地址(IP + 端口) | 服务器/客户端 IPv4 地址设置 |
| IPv4 地址(内嵌) | in_addr | 32bit IPv4 地址 | sockaddr_in.sin_addr 的类型 |
| IPv6 地址 | sockaddr_in6 | IPv6 套接字地址(IP + 端口 + flow/scope) | IPv6 服务器/客户端地址绑定 |
| IPv6 地址(内嵌) | in6_addr | 128bit IPv6 地址 | sockaddr_in6.sin6_addr 的类型 |
| Unix 域地址 | sockaddr_un | Unix 域套接字地址(路径名或抽象地址) | 本地进程间通信(本地套接字) |
| 多路复用 | pollfd | poll 监视的文件描述符和事件 | poll() 调用 |
| 多路复用 | epoll_event | epoll 事件结构和用户数据 | epoll_ctl/epoll_wait |
| 分散/聚集 I/O | iovec | 一段缓冲区的地址和长度 | readv/writev,sendmsg/recvmsg |
| 高级消息 I/O | msghdr | sendmsg/recvmsg 的消息头(地址、向量、控制信息) | 带外数据、传递文件描述符等 |
| 控制消息(辅助数据) | cmsghdr | 控制信息头部,用于 msg_control 中 | 传递 fd、IP_TTL 等控制信息 |
| 名字解析(现代) | addrinfo | getaddrinfo 返回的地址链表 | 协议无关的 DNS 和服务查询 |
| 名字解析(旧) | hostent | 主机名/地址信息(gethostbyname 等) | 已废弃,建议用 addrinfo |
| 服务查询 | servent | 服务名/端口映射(getservbyname 等) | 查询 /etc/services |
| 套接字选项 | linger | SO_LINGER 选项,控制 close 行为 | 设置优雅/强制关闭连接 |
| 时间/超时 | timeval | 秒 + 微秒时间表示 | setsockopt 超时、gettimeofday |
| 网卡接口 | ifreq | 单个网卡配置/状态(IP、掩码、标志等) | ioctl 获取/设置网卡参数 |
| 网卡接口 | ifconf | 所有网卡接口信息列表 | ioctl(SIOCGIFCONF) 获取所有网卡 |
补充
典型的代码流程
TCP服务端
socket() → bind() → listen() → accept() → recv()/send() → close()
TCP客户端
socket() → connect() → send()/recv() → close()
UDP服务端
socket() → bind() → recvfrom()/sendto() → close()
UDP客户端
socket() → sendto()/recvfrom() → close()
相关头文件
一、核心 socket 头文件
| 头文件 | 主要用途 / 说明 | 代表性函数 / 类型 |
|---|---|---|
<sys/socket.h> | 核心 socket API:创建/连接/收发等 | socket(), bind(), listen(), accept(), connect(), recv(), send(), recvmsg(), sendmsg(), getsockopt(), setsockopt(), socklen_t 等 |
<sys/types.h> | 各种基本类型(很多历史代码会包含它) | size_t, ssize_t, socklen_t 等类型定义;老式 man 里常见 #include <sys/types.h> |
<netinet/in.h> | IPv4/IPv6 地址结构、常量 | struct sockaddr_in, struct sockaddr_in6, struct in_addr, INADDR_ANY, INADDR_LOOPBACK, IPPROTO_TCP, IPPROTO_UDP 等 |
<arpa/inet.h> | 网络/主机字节序转换、IP 文本与二进制转换 | htonl(), htons(), ntohl(), ntohs(), inet_pton(), inet_ntop(), inet_addr(), inet_ntoa() 等 |
<sys/un.h> | Unix 域套接字地址结构 | struct sockaddr_un,用于本地进程间通信 |
二、I/O 多路复用相关头文件
| 头文件 | 主要用途 / 说明 | 代表性函数 / 类型 |
|---|---|---|
<sys/select.h> | select() / pselect() 及 fd_set 操作 | select(), pselect(), fd_set, FD_ZERO, FD_SET, FD_CLR, FD_ISSET |
<poll.h> | poll() / ppoll() | struct pollfd, poll() |
<sys/epoll.h> | Linux 特有的 epoll 系列 | epoll_create(), epoll_create1(), epoll_ctl(), epoll_wait(), struct epoll_event |
三、名字解析 / 服务查询头文件
| 头文件 | 主要用途 / 说明 | 代表性函数 / 类型 |
|---|---|---|
<netdb.h> | 主机名、服务名解析(现代/老式都有) | getaddrinfo(), freeaddrinfo(), gai_strerror(), getnameinfo();老式:gethostbyname(), gethostbyaddr(), getservbyname(), struct hostent, struct servent |
<sys/socket.h> | 与 getaddrinfo() 一起使用(地址结构、socklen_t 等) | 配合 <netdb.h> 一起使用 |
<sys/types.h> | 配合 <netdb.h> 的老式代码 | 老式写法常见组合:<sys/types.h> + <sys/socket.h> + <netdb.h> |
四、通用 I/O、错误处理与信号头文件
这些虽然不是“网络专用”,但在网络编程里几乎必用。
| 头文件 | 主要用途 / 说明 | 代表性函数 / 类型 |
|---|---|---|
<unistd.h> | POSIX 标准 I/O:文件描述符读写、关闭、管道等 | read(), write(), close(), pipe(), dup2() 等 |
<fcntl.h> | 文件控制:设置非阻塞、文件锁等 | fcntl(), open(),常用来设置 O_NONBLOCK |
<errno.h> | 错误码 errno 及各种 E* 常量 | errno, EINTR, EAGAIN, EWOULDBLOCK, ECONNRESET, EPIPE 等 |
<signal.h> | 信号处理(尤其是 SIGPIPE、SIGCHLD 等) | signal(), sigaction(), SIGPIPE, SIG_IGN 等 |
五、其他常用但易忽略的头文件
| 头文件 | 主要用途 / 说明 | 代表性函数 / 类型 |
|---|---|---|
<string.h> | 内存 / 字符串操作 | memset(), memcpy(), strlen(), strcmp() 等;经常用来初始化 sockaddr_in 等结构体 |
<stdio.h> | 标准 I/O / 错误输出 | perror(), fprintf(stderr, ...);网络层通常用 unistd.h 的 read/write,但调试日志经常用 stdio |
<stdlib.h> | 通用工具:内存分配、进程控制 | malloc(), free(), exit(), atoi() 等 |
<time.h> | 时间相关(超时、日志时间戳) | time(), localtime(), strftime();struct timeval 在 <sys/time.h> 或 <sys/select.h> 中定义 |
<sys/time.h> | struct timeval 等时间结构 | 常用于 select() 超时、gettimeofday()(有些实现也放在 <sys/select.h> 中) |
<sys/ioctl.h> | 设备控制,包括网卡配置等 | ioctl(),配合 SIOCGIFADDR、SIOCGIFFLAGS 等操作网卡 |

2519

被折叠的 条评论
为什么被折叠?



