Может ли select () вернуть готовый сокет без данных в очереди? - PullRequest
0 голосов
/ 21 сентября 2018

Я читаю сетевое программирование UNIX Р.Стевенса, и из него взят следующий фрагмент кода только с соответствующими частями.Это эхо-сервер.

fd_set rset, allset;
int nready, client[FD_SETSIZE];
...
for( ; ; ) {
    rset = allset;
    nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
    ....
    for( i = 0; i <= maxi; i++){
        if ( (sockfd = client[i]) <= 0)
            continue;
        if (FD_ISSET(sockfd, &rset)){
            if( (n = read(sockfd, buf, MAXLINE)) == 0){
                close(sockfd);
                FD_CLEAR(sockfd, &allset);
                client[i] = -1;
            } else
                writen(sockfd, buf, n);
            ...
            }
        }

Я кратко опишу переменные: client - массив, содержащий дескрипторы файлов, назначенные подключенным клиентам;-1 представляет бесплатный вход.nready - это число fd s, готовых к считыванию.rset - это структура, содержащая биты, которые указывают, какой fd готов.allset - это структура того же типа, представляющая fd s, которые должны быть проверены select().

Внутренний цикл for проверяет входящие данные от каждого подключенного клиента (это проверяется с помощью макроса FD_ISSSET).Если есть ожидающие данные, сервер записывает их обратно клиенту.Если read() возвращает 0, это означает, что клиент отправил FIN на сервер, поэтому он разрывает соединение с close().

Теперь вопрос : автор говорит, чтоесть проблема с сервером, который я только что показал.Действительно, рассмотрим вредоносный клиент, который подключается к серверу, отправляет один байт данных, отличный от новой строки, а затем переходит в спящий режим.Сервер вызовет read(), считывая этот байт, а затем заблокирует следующий вызов на read(), ожидая получения дополнительных данных от этого клиента, тем самым отказывая в обслуживании всем другим клиентам.Я не понимаю, почему сервер должен блокировать при следующем вызове read(): перед следующим вызовом read(), select() вернет состояния готовности для каждого подключенного сокета, и так как наш клиент не отправляет большеданные (этот один байт уже использовался), бит для состояния готовности этого клиента не установлен.Следовательно, блок if для клиента не вводится и read() не вызывается.Это нормально, если только select() не может вернуть готовый сокет без данных, поставленных в очередь, что я считаю невозможным.Так где я не прав?

...