Я читаю сетевое программирование 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()
не может вернуть готовый сокет без данных, поставленных в очередь, что я считаю невозможным.Так где я не прав?