Linux 网络编程中用于配置套接字的选项,它允许开发者精细控制套接字的行为。
头文件 #include <sys/socket.h>
。
setsockopt()
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
。
一般在 bind()
/listen()
/connect()
前设置选项。
sockfd
套接字描述符。
level
选项协议层。
协议层 | 说明 | 适用场景 |
---|---|---|
SOL_SOCKET | 通用套接字选项 | 所有套接字类型 |
IPPROTO_TCP | TCP 协议选项 | TCP 套接字 |
IPPROTO_IP | IPv4 协议选项 | IPv4 套接字 |
IPPROTO_IPV6 | IPv6 协议选项 | IPv6 套接字 |
optname
具体选项名称。
SOL_SOCKET
选项 | 数据类型 | 用途 | 示例代码 |
---|---|---|---|
SO_REUSEADDR | int | 允许重用本地地址(解决 "Address already in use" 问题) | int reuse = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); |
SO_REUSEPORT | int | 允许多进程绑定相同端口(Linux 3.9+) | int reuse = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)); |
SO_KEEPALIVE | int | 启用 TCP 心跳检测(防连接超时) | int keepalive = 1; setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)); |
SO_RCVBUF | int | 设置接收缓冲区大小(单位:字节) | int size = 1024 * 1024; setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); |
SO_SNDBUF | int | 设置发送缓冲区大小 | int size = 2048 * 1024; setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); |
SO_RCVTIMEO | struct timeval | 设置接收超时时间 | struct timeval tv = {5, 0}; // 5秒 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); |
SO_LINGER | struct linger | 控制 close() 行为(强制发送剩余数据) | struct linger ling = {1, 10}; // 等待10秒 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); |
IPPROTO_TCP
选项 | 数据类型 | 用途 | 示例代码 |
---|---|---|---|
TCP_NODELAY | int | 禁用 Nagle 算法(减少延迟,适合实时应用) | int nodelay = 1; setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)); |
TCP_KEEPIDLE | int | 首次发送心跳包前的空闲时间(秒) | int idle = 60; setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)); |
TCP_KEEPINTVL | int | 心跳包发送间隔(秒) | int interval = 10; setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)); |
TCP_KEEPCNT | int | 断开连接前的最大心跳失败次数 | int cnt = 5; setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)); |
IPPROTO_IP
选项 | 数据类型 | 用途 | 示例代码 |
---|---|---|---|
IP_TTL | int | 设置数据包生存时间(TTL) | int ttl = 64; setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); |
IP_MULTICAST_IF | struct in_addr | 设置多播输出接口 | struct in_addr addr; inet_pton(AF_INET, "192.168.1.2", &addr); setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)); |
opval
指向选项值的指针(类型取决于选项)。
oplen
optval
缓冲区的长度。
return
返回值: 成功返回 0, 失败返回 -1 并设置 errno
。
错误码 | 原因 |
---|---|
EBADF | sockfd 不是有效的文件描述符 |
ENOTSOCK | sockfd 是文件描述符,但不是套接字类型 |
EINVAL | 以下情况之一: 1. 无效的 level 或 optname 2. optval 值非法3. 选项在连接后设置(如 TCP 连接后设置 SO_REUSEADDR ) |
ENOPROTOOPT | 协议层不支持该选项(如 UDP 套接字设置 TCP_NODELAY ) |
EFAULT | optval 指针指向不可访问的内存区域 |
ENOBUFS | 系统资源不足(罕见) |
getsockopt()
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
。
获取套接字选项当前值,与 setsockopt()
相对应。它允许开发者查询套接字的当前配置状态。
sockfd
套接字描述符。
level
选项协议层。
optname
具体选项名称。
opval
输出参数:指向存储选项值的缓冲区。
oplen
输入输出参数:输入时为缓冲区大小,输出时为实际值长度。
return
返回值: 成功返回 0, 失败返回 -1 并设置 errno
。
特殊选项
SO_ERROR
获取待处理错误。
使用场景:
- 在非阻塞 connect 后检查连接状态。
- 在 I/O 多路复用中检测套接字错误。
int sock_error;
socklen_t len = sizeof(sock_error);
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &sock_error, &len) == 0) {
if (sock_error != 0) {
printf("Socket error: %s\n", strerror(sock_error));
} else {
printf("No pending socket errors\n");
}
}
SO_TYPE
获取套接字类型。
int sock_type;
socklen_t len = sizeof(sock_type);
if (getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &sock_type, &len) == 0) {
switch (sock_type) {
case SOCK_STREAM:
printf("TCP socket\n"); break;
case SOCK_DGRAM:
printf("UDP socket\n"); break;
case SOCK_RAW:
printf("Raw socket\n"); break;
default:
printf("Unknown socket type: %d\n", sock_type);
}
}
SO_ACCEPTCONN
检查是否处于监听状态。
int is_listening;
socklen_t len = sizeof(is_listening);
if (getsockopt(sockfd, SOL_SOCKET, SO_ACCEPTCONN, &is_listening, &len) == 0) {
printf("Socket is %s\n", is_listening ? "listening" : "not listening");
}
TCP_INFO
获取 TCP 连接信息。
#include <netinet/tcp.h>
struct tcp_info info;
socklen_t len = sizeof(info);
if (getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, &len) == 0) {
printf("TCP state: %s\n", tcp_state_name(info.tcpi_state));
printf("Retransmits: %u\n", info.tcpi_retransmits);
printf("RTT: %u us\n", info.tcpi_rtt);
printf("RTO: %u us\n", info.tcpi_rto);
}
Comments NOTHING