即使积压队列已满,客户端仍然连接到TCP迭代服务器

这是我创build的一个迭代服务器,用于处理基本的客户端 – 服务器聊天应用程序。

我正在尝试在一个terminal窗口上运行TCPserver ,在多个terminal窗口上运行TCPclient

超过5个客户端正在连接(既不阻塞也不失败,它们立即connect成功),尽pipe我将服务器套接字中的backlog值( listen system call)设置为5。

我预计不超过5个客户端可以连接(一次只能接受1个客户端)。

我对listen系统调用中设置的积压值的理解是否错误? 请澄清。

int listen(int sockfd,int backlog);

backlog参数定义了sockfd挂起的连接队列可以增长的最大长度。

这里是实际的程序供参考。

TCPserver.c

 #include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<unistd.h> #define BACKLOG 5 #include <netinet/in.h> #include <arpa/inet.h> #include<string.h> int main() { //create the server socket int sd; sd=socket(AF_INET,SOCK_STREAM,0); if(sd==-1) { perror("Some error occured in creating the socket: "); //Interprets the value of errno as an error message, and prints it to stderr exit(EXIT_FAILURE); } else printf("Socket created!\n"); //define the server address struct sockaddr_in server_address; server_address.sin_family = AF_INET; server_address.sin_port = htons(9002); server_address.sin_addr.s_addr = INADDR_ANY; //inet_addr("192.168.137.163");//INADDR_ANY; //bind the socket to our specified IP and port int bind_status = bind(sd, (struct sockaddr *) &server_address, sizeof(server_address)); if(bind_status == -1) { perror("An error occurred in binding the socket: " ); exit(EXIT_FAILURE); } else printf("Bind Successful!\n"); //listen for connections int listen_status = listen(sd, BACKLOG); if(listen_status == -1) { perror("Error occured in listening: "); exit(EXIT_FAILURE); } else printf("Server is listening!\n"); while(1) { //Accept a connection and create a new socket for this connection int new_sd; struct sockaddr_in client_address; int client_address_size = sizeof(client_address); new_sd= accept(sd, (struct sockaddr*) &client_address, &client_address_size); if(new_sd==-1) { perror("Can't accept connection: "); exit(EXIT_FAILURE); } else printf("Accept successful!\nA new client has connected. He'll soon send you a message.\n (You can chat or say \"exit\" to stop chatting)\n\n"); //send a message to the client char buffer[256] = "Welcome to the server, lets chat! \n (You can chat or say \"exit\" to stop chatting)\n"; send(new_sd,buffer,sizeof(buffer),0); //start chat while(1) { memset(buffer,0,256); int n = recv(new_sd, buffer, sizeof(buffer),0); if(n==-1 || strcmp(buffer,"exit\n")==0 || strcmp(buffer,"exit")==0) break; printf("\nclient said: %s\n",buffer); memset(buffer,0,256); printf("Say something: "); fgets(buffer,256,stdin); n = send(new_sd, buffer, sizeof(buffer), 0); if(n==-1 || strcmp(buffer,"exit\n")==0) break; } //close the sockets close(new_sd); printf("\nConnection ended. waiting for new connection now . . .\n"); } close(sd); return 0; } 

这是客户端

TCPclient.c

 #include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include<string.h> int main() { //create the socket int sd = socket(AF_INET,SOCK_STREAM,0); if(sd==-1) { perror("Some error occurred in creating the socket: "); exit(EXIT_FAILURE); } else printf("Socket created!\n"); //specify an address for the socket struct sockaddr_in server_address; server_address.sin_family = AF_INET; server_address.sin_port = htons(9002); server_address.sin_addr.s_addr = inet_addr("127.0.0.1");//INADDR_ANY; //Connect to the server int connection_status = connect(sd,(struct sockaddr *) &server_address, sizeof(server_address)); if(connection_status == -1) { perror("There was an error connecting to the remote socket: "); exit(EXIT_FAILURE); } else printf("Connected to the server! Waiting in the queue for the server to accept the connection...\n"); char buffer[256]; //get the connection message from the server recv(sd, &buffer, sizeof(buffer), 0); printf("Server Said: %s\n", buffer); //start chat while(1) { memset(buffer,0,256); printf("Say something: "); fgets(buffer,256,stdin); int n = send(sd,buffer,sizeof(buffer),0); if(n==-1 || strcmp(buffer,"exit\n")==0) break; memset(buffer,0,256); n = recv(sd,buffer,sizeof(buffer),0); if(n==-1 || strcmp(buffer,"exit\n")==0) break; printf("\nServer said: %s\n",buffer); } //close the socket close(sd); return 0; } 

有人build议我检查我的系统上是否已经启用了syncookies 。 当我执行cat /proc/sys/net/ipv4/tcp_syncookies ,我得到1

您的系统已启用SYN cookie ,允许TCP堆栈的行为就好像它有一个非常大的侦听队列。 它被设计为通过SYN泛洪来缓解DOS。

listen手册页说明backlog参数:

当启用syncookies ,没有逻辑最大长度,这个设置被忽略。

如果您确实希望不超过5个挂起的客户端在服务器上等待,则必须手动维护您自己的队列,并在队列已满时关闭新的连接。

请注意,此解决方案实际上不会影响操作系统侦听队列的行为。 解决方案是不断清除任何积压的监听队列,并关闭这些连接,如果你的服务器已经在自己的队列中有5个挂起的连接。

可能在你的情况下,最简单的方法是用两个线程。 一个正在接受,另一个正在处理连接。

下面的片段以伪代码的形式说明了这一点。 它假设正确的线程互斥和信令由队列操作执行。

 accepting_thread () { int queue_count = 0; for (;;) { new_conn = accept(); if (q_size(q) < 5) { q_enqueue(q, new_conn); } else { close(new_conn); } } } handling_thread () { for (;;) { new_conn = q_dequeue(q); /* ... */ close(new_conn); } } 

要让提前终止的客户端看到重置,可以使用0超时值启用延迟选项。 大多数TCP堆栈将在套接字关闭时产生RST。

待办事项是“ 待处理连接队列” – 一旦你接受了一个连接,它就不再处于待处理状态,并且从队列中离开,留下5个待处理连接的空间。

如果你想限制到5个连接,那么你需要计算你接受了多少(而不是关闭)。 任何更多的连接尝试将在队列中等待。