2015-01-26 3 views
0

В книге «UNIX Network Prgramming» 3rd, Vol 1, Section 6.8 «Сервер эхолота TCP (Revisited)» главы 6 «Мультиплексирование ввода-вывода: функции выбора и опроса» , в книге написано:неподдерживаемый однопоточный сервер с выбором

«К сожалению, есть проблема с сервером, который мы только что показали. Подумайте, что произойдет, если вредоносный клиент подключится к серверу, отправляет один байт данных (кроме новой строки), а затем отправляется спать. Сервер будет вызывать чтение, которое будет считывать один байт данных от клиента, а затем блокировать следующий вызов для чтения, ожидая большего количества данных от этого клиента. Затем сервер блокируется («висит» может быть лучший термин) «этим одним клиентом и не будет обслуживать других клиентов (либо новое клиентское соединение, либо данные существующих клиентов), пока maliciou s либо отправляет новую строку, либо завершает свою работу ».

Однако я сомневаюсь, что это не так, как описано в книге. Если «вредоносный» клиент спящий, когда второй раз вызывается функция select(), соответствующий дескриптор сокета не будет находиться в состоянии готовности к чтению, так что функция read() никогда не получит возможность блокировать одиночный -потребованный сервер. Чтобы убедиться в этом, я запускаю образец сервера и «вредоносного» клиента только для того, чтобы обнаружить, что сервер не заблокирован и соответствует другим клиентам.

Я признаю, что при объединении с вызовами мультиплексирования ввода-вывода, такими как select() или epoll(), рекомендуется использовать неблокирующий ввод-вывод. Но мой вопрос: есть ли что-то не так с выводами книги? Или есть условия, которые могут произойти в реальных приложениях, но не в этих простых примерах? Или что-то не так с моим кодом? Большое спасибо!

пример кода сервера (tcpservselect01.c):

#include "unp.h" 
int 
main(int argc, char **argv) 
{ 
    int  i, maxi, maxfd, listenfd, connfd, sockfd; 
    int  nready, client[FD_SETSIZE]; 
    ssize_t n; 
    fd_set rset, allset; 
    char buf[MAXLINE]; 
    socklen_t clilen; 
    struct sockaddr_in cliaddr, servaddr; 

    listenfd = Socket(AF_INET, SOCK_STREAM, 0); 
    bzero(&servaddr, sizeof(servaddr)); 
    servaddr.sin_family  = AF_INET; 
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    servaddr.sin_port  = htons(SERV_PORT); 

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); 

    Listen(listenfd, LISTENQ); 

    maxfd = listenfd;   /* initialize */ 
    maxi = -1;     /* index into client[] array */ 
    for (i = 0; i < FD_SETSIZE; i++) 
     client[i] = -1;   /* -1 indicates available entry */ 
    FD_ZERO(&allset); 
    FD_SET(listenfd, &allset); 
    for (; ;) { 
     rset = allset;  /* structure assignment */ 
     nready = Select(maxfd+1, &rset, NULL, NULL, NULL); 

     if (FD_ISSET(listenfd, &rset)) {/* new client connection */ 
      clilen = sizeof(cliaddr); 
      connfd = Accept(listenfd, (SA *) &cliaddr, &clilen); 

      for (i = 0; i < FD_SETSIZE; i++) 
       if (client[i] < 0) { 
        client[i] = connfd; /* save descriptor */ 
        break; 
       } 
      if (i == FD_SETSIZE) 
       err_quit("too many clients"); 

      FD_SET(connfd, &allset);/* add new descriptor to set */ 
      if (connfd > maxfd) 
       maxfd = connfd;   /* for select */ 
      if (i > maxi) 
       maxi = i;   /* max index in client[] array */ 

      if (--nready <= 0) 
       continue;  /* no more readable descriptors */ 
     } 

     for (i = 0; i <= maxi; i++) {/* check all clients for data */ 
      if ((sockfd = client[i]) < 0) 
       continue; 
      if (FD_ISSET(sockfd, &rset)) { 
       if ((n = Read(sockfd, buf, MAXLINE)) == 0) { 
        /*4connection closed by client */ 
        Close(sockfd); 
        FD_CLR(sockfd, &allset); 
        client[i] = -1; 
       } else 
        Writen(sockfd, buf, n); 

       if (--nready <= 0) 
        break; /* no more readable descriptors */ 
      } 
     } 
    } 
} 

"вредоносный" код клиента

#include "unp.h" 

void 
sig_pipe(int signo) 
{ 
    printf("SIGPIPE received\n"); 
    return; 
} 

int 
main(int argc, char **argv) 
{ 
    int     sockfd; 
    struct sockaddr_in servaddr; 

    if (argc != 2) 
     err_quit("usage: tcpcli <IPaddress>"); 

    sockfd = Socket(AF_INET, SOCK_STREAM, 0); 

    bzero(&servaddr, sizeof(servaddr)); 
    servaddr.sin_family = AF_INET; 
    servaddr.sin_port = htons(9877); 
    Inet_pton(AF_INET, argv[1], &servaddr.sin_addr); 

    Signal(SIGPIPE, sig_pipe); 

    Connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); 

    Write(sockfd, "h", 1); 
    printf("go to sleep 20s\n"); 
    sleep(20); 
    printf("wake up\n"); 
    printf("go to sleep 20s\n"); 
    Write(sockfd, "e", 1); 
    sleep(20); 
    printf("wake up\n"); 

    exit(0); 
} 

ответ

0

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

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