C++ 在重复连接尝试时无限期地接受()块
我正在编程一个TCP服务器,我希望一次只接受一个连接,并通过重用它用于侦听的地址和端口。与服务器已启动实例的第一次连接(例如通过netcat)始终成功,但随后的连接尝试在accept()处停止,而不返回套接字描述符。我尝试了不同的队列长度,以及在前一个连接处于TIME_WAIT状态时连接,以及在它被清除后连接,但结果是相同的。netcat和netstat都报告新连接尝试成功,并报告连接已建立(无论上一个连接是否在TIME\u WAIT或过期),但我的服务器在accept()调用时卡住,因此它没有注册新连接。这种行为并不总是在第一次连接尝试时立即发生,但几乎总是在前三次连接尝试时发生 守则:C++ 在重复连接尝试时无限期地接受()块,c++,linux,sockets,tcp,netstat,C++,Linux,Sockets,Tcp,Netstat,我正在编程一个TCP服务器,我希望一次只接受一个连接,并通过重用它用于侦听的地址和端口。与服务器已启动实例的第一次连接(例如通过netcat)始终成功,但随后的连接尝试在accept()处停止,而不返回套接字描述符。我尝试了不同的队列长度,以及在前一个连接处于TIME_WAIT状态时连接,以及在它被清除后连接,但结果是相同的。netcat和netstat都报告新连接尝试成功,并报告连接已建立(无论上一个连接是否在TIME\u WAIT或过期),但我的服务器在accept()调用时卡住,因此它没有
main() {
Socket socket(10669);
while (true) {
socket.establish_connection();
socket.receive(callback);
socket.close_connection();
}
}
void Socket::establish_connection() {
// Creating socket file descriptor
int server_fd = 0;
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
throw ...;
}
// Setting socket options
int socket_options = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &socket_options, sizeof(socket_options))) {
throw ...;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(server_fd, (sockaddr *) &address, sizeof(address)) < 0) {
throw ...;
}
if (listen(server_fd, 1) < 0) {
throw ...;
}
spdlog::info("Listening for clients on port {}", port);
// this is where it blocks at repeated connection attempts
struct sockaddr_in client_address;
int addrlen = sizeof(client_address);
if ((socket = accept(server_fd, (sockaddr *) &client_address, (socklen_t*) &addrlen)) < 0) {
throw ...;
}
spdlog::info("Client connected\n");
}
void Socket::receive(SocketCallback callback) {
while (true) {
fd_set read_socket_fd;
FD_ZERO(&read_socket_fd);
FD_SET(socket, &read_socket_fd);
int sel = select(socket+1, &read_socket_fd, NULL, NULL, NULL);
if (sel > 0) {
// receiving data, no problems here
}
}
}
void Socket::close_connection() {
close(socket);
}
启动时(netstat):
第一次连接时(服务器):
第一次连接时(netstat):
第一次断开与客户端(服务器)的连接时:
第一次断开与客户端(netstat)的连接时:
在第二次连接尝试时,服务器不报告任何内容,因为它卡在“侦听客户端…”行上,表示在accept()处被阻止。这是netstat报告的内容(这是我在第一次断开连接后立即连接的情况,因此,当上一次连接处于TIME\u WAIT状态时):
当我完成等待时间到期,然后尝试连接时,也会发生同样的情况:
tcp 0 0 0.0.0.0:10669 0.0.0.0:* LISTEN
tcp 1 0 0.0.0.0:10669 0.0.0.0:* LISTEN
tcp 0 0 localhost:10669 localhost:55134 ESTABLISHED
tcp 0 0 localhost:55134 localhost:10669 ESTABLISHED
在这两种情况下,netcat中的连接都处于活动状态,我可以自由键入,但当然没有收到任何内容;没有其他进程可以拦截连接
我知道我可能会尝试使用非阻塞accept(),但是accept()的阻塞行为非常适合我的用法,当它的行为符合预期时,所以问题是-为什么它会在重新连接时阻塞,我在这里缺少什么?您应该创建一个服务器套接字,然后在同一套接字上重复调用
accept
。每次调用accept
时,您似乎都在创建一个新的服务器套接字,并将旧的套接字保持打开状态
通常情况下,这是无效的,但是您使用了SO\u REUSEPORT
来告诉操作系统您确实需要它。使用SO\u REUSEPORT
。显然,操作系统选择将您的新连接发送到第一个套接字,然后您尝试从第二个套接字接受它,而第二个套接字没有等待新连接
若要修复此问题,请创建一个服务器套接字,然后始终从同一套接字
accept
。您必须建立连接,直到在单独的函数中建立连接,并在while(true)循环外仅调用一次。然后在循环中重用套接字以接受连接。应该在while循环中调用Listen和accept。一个好的做法是为每个新连接启动一个线程,但不是一个问题,可以在单个线程中完成;现在它按预期工作。
tcp 0 0 0.0.0.0:10669 0.0.0.0:* LISTEN
[2020-07-07 13:34:35.481] [info] Client connected
tcp 0 0 0.0.0.0:10669 0.0.0.0:* LISTEN
tcp 0 0 localhost:54860 localhost:10669 ESTABLISHED
tcp 0 0 localhost:10669 localhost:54860 ESTABLISHED
[2020-07-07 13:35:47.903] [warning] Client disconnected
[2020-07-07 13:35:47.903] [info] Listening for clients on port 10669
tcp 0 0 0.0.0.0:10669 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:10669 0.0.0.0:* LISTEN
tcp 0 0 localhost:54860 localhost:10669 TIME_WAIT
tcp 0 0 0.0.0.0:10669 0.0.0.0:* LISTEN
tcp 1 0 0.0.0.0:10669 0.0.0.0:* LISTEN
tcp 0 0 localhost:54968 localhost:10669 TIME_WAIT
tcp 0 0 localhost:54970 localhost:10669 ESTABLISHED
tcp 0 0 localhost:10669 localhost:54970 ESTABLISHED
tcp 0 0 0.0.0.0:10669 0.0.0.0:* LISTEN
tcp 1 0 0.0.0.0:10669 0.0.0.0:* LISTEN
tcp 0 0 localhost:10669 localhost:55134 ESTABLISHED
tcp 0 0 localhost:55134 localhost:10669 ESTABLISHED