Читаем О чём не пишут в книгах по Delphi полностью

 SockError := WSAGetSelectError(Msg.lParam);

 if SockError <> 0 then

 begin

  // Здесь должен быть анализ ошибки

  closesocket(Sock);

  Exit;

 end;

 case WSAGetSelectEvent(Msg.lParam) of

 FD_READ: begin

  // Пришел запрос от клиента. Необходимо прочитать данные,

  // сформировать ответ и отправить его.

 end;

 FD_АССЕРТ: begin

  // Просто вызываем функция accept. Ее результат нигде не

  // сохраняется, потому что вновь созданный сокет автоматически

  // начинает работать в асинхронном режиме, и его дескриптор

  // при необходимости будет передан через Msg.wParam при

  // возникновение события

  accept(Sock, nil, nil);

 end;

 FD_CLOSE:

 begin

  // Получив от клиента сигнал завершения, сервер, в принципе,

  // может попытаться отправить ему данные. После этого сервер

  // также должен закрыть соединение со своей стороны

  shutdown(Sock, SD_SEND);

  closesocket(Sock);

 end;

 end;

end;


end.

Преимущество такого сервера по сравнению с сервером, основанным на функции select, заключается в том, что он не должен постоянно проверять наличие полученных данных — когда данные поступят, он без дополнительных усилий получит уведомление об этом. Кроме того, этот сервер не имеет проблем, связанных с количеством сокетов в множестве типа TFDSet. Впрочем, последнее несущественно, т.к. при таком количестве клиентов сервер обычно реализует другие, более производительные способы взаимодействия с клиентами.

2.2.6. Пример сервера, основанного на сообщениях

В этом разделе мы напишем сервер, использующий асинхронные сокеты и их сообщения (пример AsyncSelectServer на компакт-диске). Этот сервер будет во многом похож на сервер на основе неблокирующих сокетов (см. разд. 2.1.16), только он не станет проверять по таймеру наличие данных в буфере и возможность отправки данных, а будет выполнять это тогда, когда поступят соответствующие сообщения.

Такая схема работы требует более осторожного подхода. По сигналу от таймера мы сами проверяем, на каком этапе в данный момент находится обмен данными с клиентом. Если, например, идет этап отправки данных, то проверять входной буфер сокета не нужно, можно оставить это до тех пор, пока не наступит этап чтения данных. При использовании сообщений приходится учитывать, что сообщение о поступлении данных в буфер сокета может прийти в любой момент, в том числе и тогда, когда обмен с клиентом находится на этапе отправки строки. По протоколу сервер не должен читать сообщение в этот момент, необходимо сначала закончить отправку, поэтому приходится данное уведомление игнорировать. Но второго уведомления система не пришлет, соответственно, после окончания отправки данных сервер должен сам вспомнить, что было уведомление, и перейти к операции чтения.

Примечание

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

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже