限制可以连接到服务器的客户端

时间:2014-01-18 15:03:59

标签: c

我在Linux下使用select()在C中创建了一个TCP客户端服务器程序。我想知道如何限制连接到服务器的客户端数量。那就是我可以在他们接受之前接受() - 编辑。

这是代码,对不起评论的语言。我仍然没有把它放到哪里。

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>

/* portul folosit */

#define PORT 2728

extern int errno;       /* eroarea returnata de unele apeluri */

/* functie de convertire a adresei IP a clientului in sir de caractere */
char * conv_addr (struct sockaddr_in address)
{
    static char str[25];
    char port[7];

    /* adresa IP a clientului */
    strcpy (str, inet_ntoa (address.sin_addr)); 
    /* portul utilizat de client */
    bzero (port, 7);
    sprintf (port, ":%d", ntohs (address.sin_port));    
    strcat (str, port);
    return (str);
}
int v[4]; //vector cu 5 tipuri de mancare
/* programul */
int main ()
{ int nrclienti=0; v[0]=0; v[1]=0; v[2]=0; v[3]=0; v[4]=0;
    int inchid=1;
    int x,max,pmax;max=v[0];pmax=0;int pref;
    struct sockaddr_in server;  /* structurile pentru server si clienti */
    struct sockaddr_in from;
    fd_set readfds;     /* multimea descriptorilor de citire */
    fd_set actfds;      /* multimea descriptorilor activi */
    struct timeval tv;      /* structura de timp pentru select() */
    int sd, client;     /* descriptori de socket */
    int optval=1;           /* optiune folosita pentru setsockopt()*/ 
    int fd;         /* descriptor folosit pentru 
                parcurgerea listelor de descriptori */
    int nfds;           /* numarul maxim de descriptori */
    int len;            /* lungimea structurii sockaddr_in */

    /* creare socket */
    if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror ("[server] Eroare la socket().\n");
        return errno;
    }

    /*setam pentru socket optiunea SO_REUSEADDR */ 
    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,&optval,sizeof(optval));

    /* pregatim structurile de date */
    bzero (&server, sizeof (server));

    /* umplem structura folosita de server */
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl (INADDR_ANY);
    server.sin_port = htons (PORT);

    /* atasam socketul */
    if (bind (sd, (struct sockaddr *) &server, sizeof (struct sockaddr)) == -1)
    {
        perror ("[server] Eroare la bind().\n");
        return errno;
    }

    /* punem serverul sa asculte daca vin clienti sa se conecteze */
    if (listen (sd, 5) == -1)
    {
        perror ("[server] Eroare la listen().\n");
        return errno;
    }

    /* completam multimea de descriptori de citire */
    FD_ZERO (&actfds);      /* initial, multimea este vida */
    FD_SET (sd, &actfds);       /* includem in multime socketul creat */

    tv.tv_sec = 1;      /* se va astepta un timp de 1 sec. */
    tv.tv_usec = 0;

    /* valoarea maxima a descriptorilor folositi */
    nfds = sd;

    printf ("[server] Asteptam la portul %d...\n", PORT);
    fflush (stdout);

    /* servim in mod concurent clientii... */

    while (inchid)
    {   /* ajustam multimea descriptorilor activi (efectiv utilizati) */
        bcopy ((char *) &actfds, (char *) &readfds, sizeof (readfds));

        /* apelul select() */
        if (select (nfds+1, &readfds, NULL, NULL, &tv) < 0)
        {
            perror ("[server] Eroare la select().\n");
            return errno;
        }
        /* vedem daca e pregatit socketul pentru a-i accepta pe clienti */
        if (FD_ISSET (sd, &readfds))
        {
            /* pregatirea structurii client */
            len = sizeof (from);
            bzero (&from, sizeof (from));

            /* a venit un client, acceptam conexiunea */
            client = accept (sd, (struct sockaddr *) &from, &len);


            /* eroare la acceptarea conexiunii de la un client */
            if (client < 0)
            {
                perror ("[server] Eroare la accept().\n");
                continue;
            }
            if (nfds < client) /* ajusteaza valoarea maximului */
            nfds = client;

            printf("[server] S-a conectat clientul cu descriptorul %d, de la adresa %s.\n",client, conv_addr (from));

            /* includem in lista de descriptori activi si acest socket */
            FD_SET (client, &actfds); 


            fflush (stdout);
        }
        /* vedem daca e pregatit vreun socket client pentru a trimite raspunsul */
        for (fd = 0; fd <= nfds; fd++)  /* parcurgem multimea de descriptori */
        {
            /* este un socket de citire pregatit? */
            if (fd != sd && FD_ISSET (fd, &readfds))
            {   //func

                char buffer[100];       /* mesajul */
                int bytes;          /* numarul de octeti cititi/scrisi */
                char msg[100];      //mesajul primit de la client 
                char msgrasp[100];        //mesaj de raspuns pentru client
                int replies=0;
                do
                {
                    //citim mesajul de la client
                    bytes = read (fd, msg, sizeof (buffer));
                    if (bytes < 0)
                    {
                        perror ("Eroare la read() de la client.\n");
                        return 0;
                    }
                    printf ("[server]Clientul cu descriptorul %d a ales mancarea %s\n",fd, msg);

                    int poz; //comanda alease introdusa in vectorul de comenzi
                    poz = msg[0] - '0';
                    v[poz-1]++;

                    //decidem felul preferat din meniu
                    printf("[server]");
                    for(x=0;x<=4;x++)
                    if (max<v[x]) {max=v[x];pmax=x;} 
                    pref=pmax+1;
                    for(x=0;x<=4;x++) printf("%d|",v[x]);
                    printf(" Val max e %d\n",pref);

                    /*pregatim mesajul de raspuns */
                    bzero(msgrasp,100);
                    sprintf(msgrasp, "%d", pref);

                    //daca clientului corespunde cu mancarea favorita, trimitem raspunsul
                    if (msg[0]==msgrasp[0]) sprintf(msgrasp, "%d", 1);
                    else sprintf(msgrasp, "%d", 0);

                    printf("[server]Trimitem %s clientului %d\n",msgrasp,fd);

                    if (bytes && write (fd, msgrasp, bytes) < 0)
                    {
                        perror ("[server] Eroare la write() catre client.\n");
                        return 0;
                    }
                    //func
                    replies++;
                }while((msgrasp[0]!=49)&&(replies<3));

                printf ("[server] S-a deconectat clientul cu descriptorul %d.\n\n",fd);
                fflush (stdout);
                close (fd);     /* inchidem conexiunea cu clientul */
                FD_CLR (fd, &actfds);/* scoatem si din multime */


            }
        }           /* for */
    }               /* while */
    close(sd);}             /* main */

2 个答案:

答案 0 :(得分:0)

好吧,如果您不需要客户端,您可能不会接受()他:)因此您可以限制当前正在处理的客户端数量。只需忽略主套接字上的事件即可。 您也可以使用TCP积压限制尚未接受的客户端队列的大小,请注意listen()的第二个参数

但通常它并不完全,呃,礼貌。也许你应该重新思考你的整个任务,并决定如何同时处理更多的客户。

答案 1 :(得分:0)

我将假设您使用select()运行非线程非分叉应用程序。如果没有,那么您将需要稍微修改一下但不多。

select()之前,您将使用fd_set将与开放会话相对应的每个fd添加到FD_SET;当你循环时,你可以数这些。您还将添加您的侦听器fd。如果打开的连接数达到最大值,则省略第二步。

请注意,稍后仍可接受开放式连接 - 请参阅backlog listen()的工作原理。