if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt"); 中的SO_REUSEPORT如何定义
时间: 2023-07-16 15:13:41 浏览: 183
`SO_REUSEPORT` 是一个 socket 选项,它允许多个 socket 绑定到相同的端口上。当一个客户端连接到该端口时,内核会将连接请求分配给其中一个已绑定的 socket 进行处理。这个选项在处理高并发连接时非常有用,因为它可以使多个进程或线程同时绑定到同一个端口上,从而实现负载均衡和故障恢复。
在使用 `setsockopt` 函数设置 `SO_REUSEPORT` 选项时,需要传递一个指向整数类型变量的指针作为最后一个参数,该变量的值应该是一个非零值。例如:
```
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
```
这里的 `opt` 变量就是用来设置 `SO_REUSEPORT` 选项的,它被赋值为 1,表示启用该选项。注意,`SO_REUSEADDR` 选项也被同时设置了,这是因为在某些系统上,`SO_REUSEPORT` 选项只有在 `SO_REUSEADDR` 选项也被设置时才能生效。
相关问题
int opt = 1; setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));代码所对应的功能详解
这段代码是用来设置socket的选项的。具体来说,它设置了SO_REUSEADDR和SO_REUSEPORT选项。
SO_REUSEADDR选项允许在bind()调用中使用已经被其他socket占用的地址。这个选项通常用于在socket关闭后立即重新启动服务器程序,而无需等待之前的连接完全断开。如果没有设置这个选项,在socket关闭后,由于TCP协议的TIME_WAIT状态,操作系统会保留地址一段时间,阻止其他socket使用。使用SO_REUSEADDR选项可以避免这个问题。
SO_REUSEPORT选项允许多个socket绑定同一个IP地址和端口号。这个选项通常用于实现高可用性的服务器程序,其中多个服务器程序可以同时监听同一个端口,以便在主服务器宕机时,备用服务器能够立即接管服务。使用SO_REUSEPORT选项可以避免端口占用的问题。需要注意的是,这个选项只在某些操作系统上可用,例如Linux 3.9以上的内核版本。
tcp_server_poll函数
`tcp_server_poll()` 函数通常用于在 TCP 服务器中处理客户端连接。它的作用是检查当前有没有待处理的客户端连接请求或者已经建立连接的客户端是否有数据待处理。它可以通过使用 `select()` 或者 `poll()` 系统调用来实现。下面是一个使用 `poll()` 的例子:
```c
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#define MAX_CLIENTS 10
#define BUFFER_SIZE 4096
int main(int argc, char *argv[]) {
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
// 创建 TCP socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置 socket 地址可重用
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
// 绑定 socket 地址
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);
}
struct pollfd clients[MAX_CLIENTS];
clients[0].fd = server_fd;
clients[0].events = POLLIN;
int n_clients = 1;
while (1) {
// 等待客户端连接或者数据到达
int activity = poll(clients, n_clients, -1);
if (activity < 0) {
perror("poll failed");
exit(EXIT_FAILURE);
}
// 处理新的客户端连接请求
if (clients[0].revents & POLLIN) {
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
printf("New client connected\n");
if (n_clients == MAX_CLIENTS) {
printf("Too many clients\n");
close(new_socket);
} else {
clients[n_clients].fd = new_socket;
clients[n_clients].events = POLLIN;
n_clients++;
}
}
// 处理已经建立连接的客户端发送的数据
for (int i = 1; i < n_clients; i++) {
if (clients[i].revents & POLLIN) {
valread = read(clients[i].fd, buffer, BUFFER_SIZE);
if (valread == 0) {
// 客户端关闭连接
close(clients[i].fd);
printf("Client disconnected\n");
clients[i] = clients[n_clients-1];
n_clients--;
} else {
// 处理客户端发送的数据
printf("%s\n", buffer);
send(clients[i].fd, buffer, strlen(buffer), 0);
memset(buffer, 0, BUFFER_SIZE);
}
}
}
}
return 0;
}
```
在 `main()` 函数中,首先创建一个 TCP socket,并设置 socket 地址可重用。然后,绑定 socket 地址,并开始监听客户端连接。
接下来,创建一个 `pollfd` 数组,用于存储所有的客户端 socket 描述符。将 `server_fd` 添加到数组中,并设置 `POLLIN` 事件,这样当有新的客户端连接请求到达时,就会触发 `POLLIN` 事件。
在 `while` 循环中,调用 `poll()` 等待客户端连接或者数据到达。如果 `POLLIN` 事件触发并且是来自 `server_fd` 的,说明有新的客户端连接请求到达,需要调用 `accept()` 来接受连接,并将新的客户端 socket 描述符添加到 `pollfd` 数组中。如果已经达到了最大客户端数,就需要关闭新的连接。如果是已经建立连接的客户端有数据到达,就需要读取数据,处理数据,并将响应数据发送回客户端。如果客户端关闭了连接,需要将其从 `pollfd` 数组中删除。
需要注意的是,`poll()` 函数的最后一个参数表示超时时间。如果设置为 -1,则表示一直等待直到有事件触发;如果设置为 0,则表示立即返回,不等待事件。如果设置为一个正整数,则表示等待指定的毫秒数后返回。
阅读全文