这是我为处理基本客户端 - 服务器聊天应用程序而创建的迭代服务器。
我正在尝试在终端窗口上运行 TCPserver ,在多个终端窗口上运行 TCPclient 。
超过5个客户端正在连接(既没有被阻止也没有失败。他们立即connect
成功)尽管我设置了backlog
值(listen
系统调用)在服务器套接字中为5。
我预计连接的客户端不能超过5个(一次只能接受1个)。
我对listen
系统调用中设置的 backlog 值的理解是否错误?
请澄清。
int listen(int sockfd,int backlog);
backlog参数定义sockfd的挂起连接队列可能增长的最大长度。
以下是实际的参考程序。
#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;
}
以及客户
#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;
}
有人建议我检查我的系统上是否已启用syncookies
。当我执行cat /proc/sys/net/ipv4/tcp_syncookies
时,我得到1
。
答案 0 :(得分:2)
积压是“待处理连接的队列” - 一旦您接受连接,它就不再处于暂挂状态,并从队列中退出,为另外5个待处理连接留出空间。
如果要限制为5个连接,则需要计算已接受(并且未关闭)的数量。然后,任何更多的连接尝试都会在队列中等待。
答案 1 :(得分:2)
您的系统已启用SYN cookies,这使得TCP堆栈的行为就像它具有非常大的侦听队列一样。它旨在通过SYN泛滥减轻DOS。
关于backlog
参数的listen
man page个州:
启用
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超时值启用linger选项。大多数TCP堆栈都会在套接字关闭时生成RST。