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

     Connection.BytesLeft := Length(Connection.Msg);

    end;

   end

   else if Res = 0 then

   begin

    AddMessageToLog('Клиент ' + Connection.ClientAddr +

     ' закрыл соединение');

    RemoveConnection;

    Exit;

   end

   else

    // Как обычно, "ошибку" WSAEWOULDBLOCK просто игнорируем

    if WSAGetLastError <> WSAEWOULDBLOCK then

    begin

     AddMessageToLog('Ошибка при получении данных от клиента ' +

      Connection.ClientAddr + ': ' + GetErrorString);

     RemoveConnection;

     Exit;

   end;

  end;

  if Connection.Phase = tpSendString then

  begin

   // Следующий этап — отправка строки. Код примерно такой же,

   // как и в предыдущем этапе, но вместо recv используется send.

   // Кроме того, отсутствует проверка на Res = 0, т.к. при

   // использовании TCP send никогда не возвращает 0.

   Res :=

    send(Connection.ClientSocket, Connection.Msg[Connection.Offset + 1],

     Connection.BytesLeft, 0);

   if Res > 0 then

   begin

    Inc(Connection.Offset, Res);

    Dec(Connection.BytesLeft, Res);

    // Если Connection.BytesLeft = 0, значит, строка отправлена

    // полностью.

    if Connection.BytesLeft = 0 then

    begin

     AddMessageToLog('Клиенту ' + Connection.ClientAddr +

      ' отправлена строка: ' + Connection.Msg);

     // Очищаем строку, престо сэкономить память

     Connection.Msg := '';

     // Следующий этап - снова получение длины строки от клиента

     Connection.Phase := tpReceiveLength;

     // Получено - 0 байт

     Connection.Offset := 0;

     // Осталось прочитать столько, сколько занимает целое число

     Connection.BytesLeft := SizeOf(Integer);

   end;

  end

  else

   if WSAGetLastError <> WSAEWOULDBLOCK then

   begin

    AddMessageToLog('Ошибка при отправке данных клиенту ' +

     Connection.ClientAddr + ': ' + GetErrorString);

    RemoveConnection;

    Exit;

   end;

 end;

end;

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

2.1.17. Параметры сокета

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

Для получения текущего значения параметров сокета предусмотрена функция getsockopt, для изменения — setsockopt. Прототипы этих функций выглядят следующим образом:

function getsockopt(s: TSocket; level, optname: Integer; optval: PChar; var optlen: Integer): Integer;

function setsockopt(s: TSocket; level, optname: Integer; optval: PChar; optlen: Integer): Integer;

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