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

时间:2016-09-14 18:43:22

标签: c linux sockets tcp

这是我为处理基本客户端 - 服务器聊天应用程序而创建的迭代服务器。

我正在尝试在终端窗口上运行 TCPserver ,在多个终端窗口上运行 TCPclient

超过5个客户端正在连接(既没有被阻止也没有失败。他们立即connect成功)尽管我设置了backlog值(listen系统调用)在服务器套接字中为5。

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

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

  

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;
}

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

2 个答案:

答案 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。