setsockopt 函数是用于设置套接字选项的系统调用,它在多种网络编程场景中非常有用。通过 setsockopt 函数,可以设置各种套接字选项。

超时

接发超时

setsockopt 函数原型

在C语言中,setsockopt 函数的原型如下:

1
2
3
4
#include <sys/types.h>
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • sockfd:要设置选项的套接字文件描述符。
  • level:选项所在的协议层,例如 SOL_SOCKET 表示套接字层,IPPROTO_TCP 表示TCP协议层。
  • optname:要设置的选项名称。
  • optval:指向包含选项值的缓冲区的指针。
  • optlen:选项值的长度。

设置 sendrecv 超时选项

要设置 sendrecv 的超时选项,可以使用 SO_SNDTIMEOSO_RCVTIMEO 选项,它们分别用于设置发送和接收的超时时间。

服务端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[1024] = {0};
const char *response = "Hello from server";

// 创建套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

// 绑定套接字
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);

if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}

// 监听连接
if (listen(server_fd, 3) < 0) {
perror("listen");
close(server_fd);
exit(EXIT_FAILURE);
}



struct timeval timeout;
// 设置接收超时选项
timeout.tv_sec = 1; // 超时时间为5秒
timeout.tv_usec = 0;

if (setsockopt(server_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
perror("setsockopt");
close(server_fd);
exit(EXIT_FAILURE);
}

// 接受连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
close(server_fd);
exit(EXIT_FAILURE);
}

// 接收数据
int bytes_received = recv(new_socket, buffer, 1024, 0);
if (bytes_received < 0) {
perror("recv");
} else {
buffer[bytes_received] = '\0';
std::cout << "Received data: " << buffer << std::endl;
}

// 发送响应
send(new_socket, response, strlen(response), 0);
std::cout << "Response sent" << std::endl;

close(new_socket);
close(server_fd);
return 0;
}

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[1024] = {0};
const char *message = "Hello from client";
struct timeval timeout;

// 创建套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

// 设置接收超时选项


// 连接服务器
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);

if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("inet_pton failed");
close(sock);
exit(EXIT_FAILURE);
}

if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("connect failed");
close(sock);
exit(EXIT_FAILURE);
}

// 发送数据
send(sock, message, strlen(message), 0);
std::cout << "Message sent" << std::endl;

// 接收响应
int bytes_received = recv(sock, buffer, 1024, 0);
if (bytes_received < 0) {
perror("recv");
} else {
buffer[bytes_received] = '\0';
std::cout << "Received response: " << buffer << std::endl;
}

close(sock);
return 0;
}

当服务器超过给定的时间,就会返回accept: Resource temporarily unavailable

TCP连接超时

TCP_USER_TIMEOUT 选项用于设置TCP连接的超时时间。如果在指定的时间内没有收到对端的确认,连接将被关闭。

1
2
3
4
5
6
int timeout_ms = 10000;  // 超时时间为10秒

if (setsockopt(sockfd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout_ms, sizeof(timeout_ms)) < 0) {
perror("setsockopt");
// 处理错误
}

close-fd超时

SO_LINGER 选项用于设置 close 操作的行为。通过设置 SO_LINGER 选项,可以控制套接字关闭时的延迟时间。

1
2
3
4
5
6
7
8
struct linger ling;
ling.l_onoff = 1; // 启用SO_LINGER选项
ling.l_linger = 5; // 延迟时间为5秒

if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) < 0) {
perror("setsockopt");
// 处理错误
}