setsockopt() 函数
setsockopt() 函数用于设置套接字选项。
语法
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
参数
sockfd
:一个有效的套接字描述符。level
:选项所在的协议级别- SOL_SOCKET:是 socket 级别的选项,所有套接字通用。
- IPPROTO_TCP:是 TCP 协议级别的选项。
- IPPROTO_UDP:是 UDP 协议级别的选项。
- IPPROTO_IP:是 IP 协议级别的选项。
optname
:要设置的选项名- SO_REUSEADDR:允许重复使用本地地址。 - SO_REUSEPORT:允许多个套接字绑定到同一端口。 - SO_KEEPALIVE:保持连接。 - SO_LINGER:关闭套接字时,是否等待数据发送完成。 - SO_RCVTIMEO:接收超时时间。 - SO_SNDTIMEO:发送超时时间。 - SO_RCVBUF:接收缓冲区大小。 - SO_SNDBUF:发送缓冲区大小。 - SO_BROADCAST:允许发送广播数据。 - SO_OOBINLINE:接收带外数据。 - SO_TYPE:获取套接字类型。
- TCP的选项:
- TCP_NODELAY:禁用 Nagle 算法。
- TCP_MAXSEG:最大报文段长度。
- TCP_CORK:启用 TCP_CORK 模式。
- TCP_DEFER_ACCEPT:延迟接受。
- TCP_INFO:获取 TCP 信息。
- TCP_QUICKACK:快速回应 ACK。
- TCP_SYNCNT:同步序列号。
- TCP_WINDOW_CLAMP:窗口大小限制。
- TCP_MAXRT:最大重传时间。
- TCP_USER_TIMEOUT:用户超时。
- TCP_CONGESTION:获取拥塞控制状态。
- IP的选项:
- IP_TOS:设置 IP 类型。
- IP_TTL:设置 IP 生存时间。
- IP_HDRINCL:包含 IP 头部。
- IP_OPTIONS:设置 IP 选项。
- IP_MULTICAST_IF:设置多播接口。
- IP_MULTICAST_TTL:设置多播 TTL。
- IP_MULTICAST_LOOP:设置多播环回。
- IP_ADD_MEMBERSHIP:加入多播组。
- IP_DROP_MEMBERSHIP:离开多播组。
- IP_BLOCK_SOURCE:阻止源地址。
- IP_UNBLOCK_SOURCE:解除阻止源地址。
- UDP的选项:
- UDP_NOCHECKSUM:禁用
- UDP_CHECKSUM:启用 UDP 校验和。
- UDP_DEFER_ACCEPT:延迟接受。
- UDP_MEM_INFO:获取 UDP 内存信息。
- UDP_MTU_DISCOVER:获取 MTU 发现。
- UDP_RECVMMSG:接收多包数据
- UDP_SENDMMSG:发送多包数据。
- UDP_SEGMENT:设置 UDP 分段。
- UDP_SNDBUF:设置发送缓冲区大小。
- UDP_RECVBUF:设置接收缓冲区大小。
- UDP_MULTICAST_IF:设置多播接口。
- UDP_MULTICAST_TTL:设置多播 TTL。
- UDP_MULTICAST_LOOP:设置多播环回。
- UDP_ADD_MEMBERSHIP:加入多播组。
- UDP_DROP_MEMBERSHIP:离开多播组。
- UDP_BLOCK_SOURCE:阻止源地址。
- UDP_UNBLOCK_SOURCE:解除阻止源地址。
- TCP的选项:
optval
:指向存放选项值的指针。optlen
:选项值的长度。
返回值
- 成功:返回 0。
- 出错:返回 -1,并设置
errno
变量。 - 注意:
setsockopt()
函数设置的选项值是临时的,只对当前套接字有效,关闭套接字后,设置的选项值就会失效。
示例
这个案例展示了如何创建一个简单的TCP服务器,并使用 setsockopt() 函数设置套接字的选项。
具体来说,它设置了套接字的 SO_REUSEADDR 和 SO_REUSEPORT 选项,以及接收数据的超时选项 SO_RCVTIMEO。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
#define BUFSIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFSIZE] = {
0};
struct timeval timeout;
// 创建套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置套接字选项
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
// 设置超时选项
timeout.tv_sec = 5; // 5秒
timeout.tv_usec = 0;
if (setsockopt(server_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
perror("setsockopt timeout");
exit(EXIT_FAILURE);
}
// 绑定套接字
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听套接字
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 接受连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 读取数据
int valread = read(new_socket, buffer, BUFSIZE);
if (valread < 0) {
perror("read");
} else {
printf("Received message: %s\n", buffer);
}
// 关闭套接字
close(new_socket);
close(server_fd);
return 0;
}
getsockopt() 函数
getsockopt() 函数用于获取套接字选项。
语法
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
参数
sockfd
:一个有效的套接字描述符。level
:选项所在的协议级别- SOL_SOCKET:是 socket 级别的选项,所有套接字通用。
- IPPROTO_TCP:是 TCP 协议级别的选项。
- IPPROTO_UDP:是 UDP 协议级别的选项。
- IPPROTO_IP:是 IP 协议级别的选项。
optname
:要获取的选项名- SO_REUSEADDR:允许重复使用本地地址。
- SO_REUSEPORT:允许多个套接字绑定到同一端口。
- SO_KEEPALIVE:保持连接。
- SO_LINGER:关闭套接字时,是否等待数据发送完成。
- SO_RCVTIMEO:接收超时时间。
- SO_SNDTIMEO:发送超时时间。
- SO_RCVBUF:接收缓冲区大小。
- SO_SNDBUF:发送缓冲区大小。
- SO_BROADCAST:允许发送广播数据。
- SO_OOBINLINE:接收带外数据。
- SO_TYPE:获取套接字类型。
optval
:指向存放选项值的指针。optlen
:指向存放选项值的长度的指针。
返回值
- 成功:返回 0。
- 出错:返回 -1,并设置
errno
变量。
示例
创建一个简单的TCP服务器,并使用 getsockopt() 函数获取套接字的选项。
具体来说,它获取了接收数据的超时选项 SO_RCVTIMEO,并打印出超时设置的秒数和微秒数。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
#define BUFSIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFSIZE] = {
0};
struct timeval timeout;
socklen_t len;
// 创建套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置套接字选项
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
// 设置超时选项
timeout.tv_sec = 5; // 5秒
timeout.tv_usec = 0;
if (setsockopt(server_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
perror("setsockopt timeout");
exit(EXIT_FAILURE);
}
// 获取超时选项
len = sizeof(timeout);
if (getsockopt(server_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, &len) < 0) {
perror("getsockopt timeout");
exit(EXIT_FAILURE);
}
printf("Timeout setting: %ld seconds, %ld microseconds\n", timeout.tv_sec, timeout.tv_usec);
// 绑定套接字
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听套接字
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 接受连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 读取数据
int valread = read(new_socket, buffer, BUFSIZE);
if (valread < 0) {
perror("read");
} else {
printf("Received message: %s\n", buffer);
}
// 关闭套接字
close(new_socket);
close(server_fd);
return 0;
}