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

Сразу отметим, что, хотя спецификация допускает частичное копирование функцией send данных в буфер сокета, на практике такое поведение наблюдать пока не удалось: все эксперименты показали, что функция send всегда либо копирует данные целиком, расширяя при необходимости буфер, либо дает ошибку WSAEWOULDBLOCK. Далее этот вопрос будет обсуждаться подробнее. Тем не менее при написании программ следует учитывать возможность частичного копирования, т.к. оно может появиться в тех условиях или в тех реализациях библиотеки сокетов, которые в наших экспериментах не были проверены.

2.1.16. Сервер на неблокирующих сокетах

В этом разделе мы создадим сервер, основанный на неблокирующих сокетах. Это будет наш первый сервер, не использующий функцию ReadFromSocket (см. листинг 2.13). Этот сервер (пример NonBlockingServer на компакт-диске) состоит из одной нити, которая никогда не будет блокироваться сокетными операциями, т.к. все сокеты используют неблокирующий режим. На форме находится таймер, по сигналам которого сервер выполняет попытки чтения данных с сокетов всех подключившихся клиентов. Если данных нет, функция recv немедленно завершается с ошибкой WSAEWOULDBLOCK, и сервер переходит к попытке чтения из следующего сокета.

Запуск сервера (листинг 2.30) мало чем отличается от запуска многонитевого сервера (см. листинг 2.19). Практически вся разница заключается в том, что вместо запуска "слушающей" нити сокет переводится в неблокирующий режим и включается таймер.

Листинг 2.30. Инициализация сервера на неблокирующих сокетах

// Реакция на кнопку "Запустить" - запуск сервера

procedure TServerForm.BtnStartServerClick(Sender: TObject);

var

 // Адрес, к которому привязывается слушающий сокет

 ServerAddr: TSockAddr;

 NonBlockingArg: u_long;

begin

 // Формируем адрес для привязки.

 FillChar(ServerAddr.sin_zero, SizeOf(ServerAddr.sin_zero), 0);

 ServerAddr.sin_family := AF_INET;

 ServerAddr.sin_addr.S_addr := INADDR_ANY;

 try

  ServerAddr.sin_port := htons(StrToInt(EditPortNumber.Text));

  if ServerAddr.sin_port = 0 then

  begin

   MessageDlg('Номер порта должен находиться в диапазоне 1-65535',

    mtError, [mbOK], 0);

   Exit;

  end;

  // Создание сокета

  FServerSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  if FServerSocket = INVALID_SOCKET then

  begin

   MessageDlg('Ошибка при создании сокета: '#13#10 + GetErrorString,

    mtError, [mbOK], 0);

   Exit;

  end;

  // Привязка сокета к адресу

  if bind(FServerSocket, ServerAddr, SizeOf(ServerAddr)) = SOCKET_ERROR then

  begin

   MessageDlg('Ошибка при привязке сокета к адреcу: '#13#10 +

    GetErrorString, mtError, [mbOK], 0);

   closesocket(FServerSocket);

   Exit;

  end;

  // Перевод сокета в режим прослушивания

  if listen(FServerSocket, SOMAXCONN) = SOCKET_ERROR then

  begin

   MessageDlg('Ошибка при переводе сокета в режим прослушивания:'#13#10 +

    GetErrorString, mtError, [mbOK], 0);

   closesocket(FServerSocket);

   Exit;

  end;

  // Перевод сокета в неблокирующий режим

  NonBlockingArg := 1;

  if ioctlsocket(FServerSocket, FIONBIO, NonBlockingArg) = SOCKET_ERROR then

  begin

   MessageDlg('Ошибка при переводе сокета в неблокирующий режим:'#13#10 +

    GetErrorString, mtError, [mbOK], 0);

   closesocket(FServerSocket);

   Exit;

  end;

  // Перевод элементов управления в состояние "Сервер работает"

  LabelPortNumber.Enabled := False;

  EditРоrtNumber.Enabled := False;

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