我试图将 UDP 和 TCP 套接字绑定到相同的 IP 地址和端口,然后处理这两种类型的连接。我已经实现了以下代码:
主函数初始化两个套接字并使用
poll()
来监视它们:
int main(int argc, char **argv) {
if (argc != 3) {
return EXIT_FAILURE;
}
struct sockaddr_in addr = derive_sockaddr(argv[1], argv[2]);
int server_socket = setup_server_socket(addr, SOCK_STREAM);
int server_socket_udp = setup_server_socket(addr, SOCK_DGRAM);
struct pollfd sockets[2] = {
{.fd = server_socket, .events = POLLIN},
{.fd = server_socket_udp, .events = POLLIN}
};
struct connection_state state = {0};
while (true) {
int ready = poll(sockets, sizeof(sockets) / sizeof(sockets[0]), -1);
if (ready == -1) {
perror("poll");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < sizeof(sockets) / sizeof(sockets[0]); i++) {
if (sockets[i].revents != POLLIN) {
continue;
}
int s = sockets[i].fd;
if (s == server_socket_udp) {
// Handle UDP
} else if (s == server_socket) {
int connection = accept(server_socket, NULL, NULL);
if (connection == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
close(server_socket);
perror("accept");
exit(EXIT_FAILURE);
} else {
connection_setup(&state, connection);
sockets[0].events = 0;
sockets[1].fd = connection;
sockets[1].events = POLLIN;
}
} else {
assert(s == state.sock);
bool cont = handle_connection(&state);
if (!cont) {
sockets[0].events = POLLIN;
sockets[1].fd = -1;
sockets[1].events = 0;
}
}
}
}
return EXIT_SUCCESS;
}
这是用于设置套接字的辅助函数:
static int setup_server_socket(struct sockaddr_in addr, int type) {
const int enable = 1;
const int backlog = 1;
int sock = socket(AF_INET, type, 0);
if (sock == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
perror("fcntl");
exit(EXIT_FAILURE);
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == -1) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
if (type == SOCK_DGRAM) {
int broadcast = 1;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) == -1) {
perror("setsockopt(SO_BROADCAST)");
close(sock);
exit(EXIT_FAILURE);
}
}
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("bind");
close(sock);
exit(EXIT_FAILURE);
} else {
fprintf(stderr, "UDP or TCP socket bound to %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
if (type == SOCK_STREAM) {
if (listen(sock, backlog)) {
perror("listen");
exit(EXIT_FAILURE);
}
}
return sock;
}
所有其他功能都可以正常工作,只是我不确定的逻辑:
将 UDP 和 TCP 套接字绑定到相同的地址和端口是否正确且安全?
poll() 逻辑是否正确处理这些套接字?具体来说,我可以依靠 poll() 来监视和区分 TCP 连接、UDP 套接字和传入数据吗?
在处理 UDP 套接字、接受 TCP 连接和处理 TCP 客户端连接之间切换时,代码中是否存在潜在问题或竞争条件?
任何见解或建议将不胜感激!
UDP 端口集与 TCP 端口集完全分开。
因此,一个套接字打开 TCP 端口 n,另一个套接字打开 UDP 端口 n 是没有问题的。