2014-01-18 5 views
0

Я сделал программу клиент-сервер TCP в C под Linux с помощью select(). Мне было интересно, как ограничить количество клиентов, подключающихся к серверу. То есть, если я могу, прежде чем они получат accept() - ed.Ограничить клиентов, которые могут подключиться к серверу

Вот код, извините, язык комментариев. Я до сих пор не понял, где это сделать.

#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 */ 
+0

Можете ли вы остановить 'listen()' ing после того, как было достаточно 'accept()' s? Это простой способ, но клиент tcp не может завершить соединение в течение длительного времени, и ответственность за это не несет сервер. - вы также можете ограничить количество входящих в очередь (ожидающих) соединений, используя второй аргумент 'listen'. – user3125280

+0

Использование журнала прослушивания не работает для меня. Я собираюсь попробовать первое. –

+0

Задержка определяет только количество подключений * Ожидание *, которое не принимается, а не количество принятых в настоящее время. – user3125280

ответ

0

Я предполагаю, что вы используете не-резьбовое приложение, не использующее forking, используя select(). Если нет, то вам нужно немного изменить это, но не сильно.

До select() вы добавите каждый из ваших fd, соответствующий открытому сеансу, на ваш fd_set, используя FD_SET; вы можете считать это, пока вы проходите. Вы также добавите слушателя fd. Просто опустите этот второй шаг, если количество открытых соединений максимально.

Обратите внимание, что открытые соединения могут быть приняты позже - см., Как backlog работает на listen().

+0

for (fd = 0; fd <= nfds; fd ++) \t // дескрипторы if (fd! = Sd && FD_ISSET (fd, & readfds)) // является готовым к чтению гнездом? ... Да, вот оно. И что же мне делать? –

+0

Если это главный сокет, просто игнорируйте его. Или просто не включайте мастер-сокет в набор 'readfds'. – user3159253

+0

Вы не можете просто игнорировать его, или вы будете ждать, когда будет ожидающее соединение (т. Е. 'Select' вернет вам что-то делать, но тогда вы ничего не сделаете, вернитесь в' select' и т. Д.). Правильное действие состоит в том, чтобы не добавлять его в 'readfds' до' select', и в этом случае у вас никогда не будет 'FD_ISSET' return true для него позже. – abligh

0

Ну, если вам не нужен клиент не может принять() его :) Таким образом, вы можете ограничить количество клиентов в данный момент обрабатывается. Просто игнорируйте события в своем главном сокете, и вы это сделали. Также вы можете ограничить размер очереди для клиентов, которые еще не были приняты с использованием TCP backlog, обратите внимание на второй параметр listen()

Но, как правило, это не совсем, эмм, вежливый. Возможно, вам следует пересмотреть всю свою задачу и решить, как обращаться с большим количеством клиентов одновременно.

+0

Сервер использует select, чтобы одновременно обрабатывать несколько клиентов. Но он имитирует ресторан, поэтому мне нужны ограниченные «столы». –

Смежные вопросы