getsockopt如何获取套接字选项?

99ANYc3cd6
预计阅读时长 21 分钟
位置: 首页 C语言 正文

这是一个在 Linux/Unix 网络编程中非常核心和常用的函数。

c语言 getsockopt
(图片来源网络,侵删)

getsockopt 是什么?

getsockopt 是 "get socket options" 的缩写,中文意思是“获取套接字选项”。

它的主要作用是查询和获取一个已创建的套接字(socket)的当前配置参数,这些参数被称为“套接字选项”(Socket Options),它们可以控制套接字的各种行为,

  • 是否启用 TCP 的 Nagle 算法(TCP_NODELAY)。
  • 设置发送或接收缓冲区的大小(SO_SNDBUF, SO_RCVBUF)。
  • 获取套接字的本地地址和端口(SO_ERROR)。
  • 获取套接字所关联的协议族(SO_DOMAIN)。
  • 设置是否允许地址重用(SO_REUSEADDR)。

getsockoptsetsockopt 是一对函数,后者用于设置套接字选项。


函数原型

getsockopt<sys/socket.h> 头文件中声明。

c语言 getsockopt
(图片来源网络,侵删)
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

参数详解

让我们逐个分析这个函数的五个参数:

参数 类型 描述
sockfd int 套接字文件描述符,这是一个通过 socket() 函数创建,并通过 bind(), listen(), connect() 等函数使用过的、有效的套接字描述符,你想查询的就是这个套接字的选项。
level int 选项级别,这个参数指定了选项所对应的协议层,常见的值有:
SOL_SOCKET: 通用套接字选项,适用于所有类型的套接字(无论 TCP, UDP, 还是原始套接字)。
IPPROTO_IP: IP 协议层选项。IP_TOS (服务类型)。
IPPROTO_TCP: TCP 协议层选项。TCP_NODELAY (禁用 Nagle 算法)。
IPPROTO_IPV6: IPv6 协议层选项。
optname int 选项名称,这是一个具体的常量,它和 level 一起唯一确定了一个要查询的选项,当 levelSOL_SOCKET 时,optname 可以是 SO_REUSEADDR;当 levelIPPROTO_TCP 时,optname 可以是 TCP_NODELAY
optval void * 选项值的缓冲区,这是一个指向变量的指针,用于存放获取到的选项值,如果查询 SO_REUSEADDRoptval 就应该指向一个 int 类型的变量,如果查询 SO_RCVBUFoptval 也应指向一个 int
optlen socklen_t * 选项值缓冲区的长度,这是一个指向 socklen_t 类型变量的指针。它有两个作用
1. 输入:在调用 getsockopt 之前,你需要将 *optlen 设置为你为 optval 分配的缓冲区的大小(对于 int 类型,设置为 sizeof(int))。
2. 输出:函数成功返回后,*optlen 会被修改为实际返回的选项值的长度,这对于某些可能返回变长数据的选项非常重要。

返回值

  • 成功:返回 0
  • 失败:返回 -1,并设置 errno 来指示具体的错误原因,常见的 errno 包括:
    • EBADF: sockfd 不是一个有效的文件描述符。
    • ENOPROTOOPT: 指定的 leveloptname 不支持或不适用于该套接字。
    • EFAULT: optvaloptlen 指向了不可访问的内存区域。
    • EINVAL: optlen 无效(指向的值为负数)。

使用示例

下面我们通过几个最常见的例子来演示如何使用 getsockopt

示例 1:获取 TCP_NODELAY 选项

TCP_NODELAY 用于禁用 Nagle 算法,Nagle 算法会小数据包缓冲合并后发送,以减少网络报文数量,但对于需要低延迟的应用(如实时游戏、金融交易),我们希望数据包能立即发送。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }
    int flag = 0; // 用于存放获取到的值
    socklen_t len = sizeof(flag); // 必须初始化为缓冲区大小
    // 获取 TCP_NODELAY 选项
    // IPPROTO_TCP 是 TCP 协议层
    // TCP_NODELAY 是选项名
    if (getsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, &len) < 0) {
        perror("getsockopt failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    printf("TCP_NODELAY option value: %d\n", flag);
    printf("Length of the value returned: %zu\n", len);
    close(sockfd);
    return 0;
}

编译和运行:

c语言 getsockopt
(图片来源网络,侵删)
gcc getsockopt_example1.c -o getsockopt_example1
./getsockopt_example1

可能输出:

TCP_NODELAY option value: 0
Length of the value returned: 4

0 表示 Nagle 算法是启用的(默认值),你可以用 setsockopt 将其设置为 1 来禁用,然后再用 getsockopt 查看值的变化。

示例 2:获取套接字错误状态

SO_ERROR 是一个非常实用的选项,它允许你获取套接字上挂起的错误,这对于 connect()accept() 操作后检查底层错误非常有用,因为它们本身可能不会返回详细的错误码。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    // 假设我们进行了一个失败的 connect 操作
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(12345); // 一个很可能没人监听的端口
    inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
    // connect 会失败,但 errno 会被设置
    if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
        // perror("connect"); // 我们不在这里打印,而是用 getsockopt 检查
    }
    int error_code = 0;
    socklen_t error_len = sizeof(error_code);
    // 获取套接字的错误状态
    if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error_code, &error_len) < 0) {
        perror("getsockopt SO_ERROR");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    if (error_code != 0) {
        // 使用 strerror 将错误码转换为可读的字符串
        printf("Socket has pending error: %s\n", strerror(error_code));
    } else {
        printf("No pending error on the socket.\n");
    }
    close(sockfd);
    return 0;
}

编译和运行:

gcc getsockopt_example2.c -o getsockopt_example2
./getsockopt_example2

可能输出:

Socket has pending error: Connection refused

这清晰地告诉我们 connect 失败的原因是“连接被拒绝”,而不是一个模糊的 "Operation failed"。


常用选项速查表

Level optname 类型 描述
SOL_SOCKET SO_REUSEADDR int 允许 bind() 重用处于 TIME_WAIT 状态的地址,常用于服务器重启。
SOL_SOCKET SO_KEEPALIVE int 启用 TCP 保活机制,如果连接在一段时间内没有数据传输,TCP 会发送探测包来检查连接是否还活着。
SOL_SOCKET SO_RCVBUF int TCP 接收缓冲区的大小(字节)。
SOL_SOCKET SO_SNDBUF int TCP 发送缓冲区的大小(字节)。
SOL_SOCKET SO_TYPE int 获取套接字的类型(SOCK_STREAM, SOCK_DGRAM)。
SOL_SOCKET SO_ERROR int 获取并清除套接字上的挂起错误。
IPPROTO_TCP TCP_NODELAY int 禁用 Nagle 算法。1 为禁用,0 为启用。
IPPROTO_TCP TCP_KEEPIDLE int 在开始发送保活探测包之前,连接必须空闲的秒数。
IPPROTO_TCP TCP_KEEPINTVL int 保活探测包之间的间隔秒数。

getsockopt 是 C 语言网络编程中一个不可或缺的工具,它让你能够:

  1. 查询配置:了解当前套接字是如何被配置的。
  2. 调试问题:通过 SO_ERROR 等选项获取底层的、详细的错误信息。
  3. 动态调整:通过结合 getsockoptsetsockopt,可以根据程序运行时的状态动态地优化套接字性能。

记住它的核心用法:提供一个套接字描述符、一个协议层、一个选项名,以及一个用来存放结果的缓冲区和它的长度,理解了这五个参数,你就能掌握 getsockopt 的精髓。

-- 展开阅读全文 --
头像
dede导航limit参数如何正确使用?
« 上一篇 01-30
dede list如何调用图片?
下一篇 » 01-30

相关文章

取消
微信二维码
支付宝二维码

目录[+]