15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Использование компонента TServerSocket.

Автор: Brian Pedersen

В Delphi документации по многопотоковому TServerSocket налито довольно много воды, и начинающему программисту сложно понять суть дела. Давайте попытаемся пролить немного света на этот раздел хелпа.

Совместимость: Delphi 3.x (или выше)

Вообще-то, создать многопотоковый сервер, который ожидает пришедшие сообщения на сокете довольно просто. В Delphi для этой цели достаточно использовать компонент TServerSocket.

Давайте рассмотрим структуру работы данного компонента:

- Добавляем TServerSocket в Вашу основную форму.
- Устанавливаем свойство Servertype в stThreadBlocking
- Создаём новый "unit" (показанный ниже) содержащий поток сервера.


Устанавливаем следующий код на OnSocketGetThread

procedure TfrmMain.fSocketGetThread(Sender: TObject;
  ClientSocket: TServerClientWinSocket;
  var SocketThread: TServerClientThread);
begin
  // Здесь создаём объект TServerThread, который я привожу ниже.
  // Новый объект создаётся каждый раз, когда когда установлен канал связи. 
  SocketThread := TServerThread.Create( FALSE, ClientSocket );
end;

TServerThread - это объект, который я создаю самостоятельно. Объект наследуется от TServerClientThread и содержит код, который обычно читает и пишет данные из/в сокет.

Созданный "unit", содержит следующий код:

unit serverthread;

interface

uses
  windows, scktcomp, SysUtils, Classes, Forms;

type
  EServerThread = class( Exception );
  // serverthread это потомок TServerClientThread
  TServerThread = class( TServerClientThread )
    private
      fSocketStream : TWinSocketStream;
    public
      procedure ClientExecute; override;
      // ClientExecute отменяет
      // TServerClientThread.ClientExecute
      // и содержит код, который
      // выполняется при старте потока
  end;

implementation

procedure TServerThread.ClientExecute;
begin
  inherited FreeOnTerminate := TRUE;
  try
    fSocketStream := TWinSocketStream.Create( ClientSocket,
                                              100000 );
    // 100000 - это таймаут в миллисекундах.
    try
      while ( not Terminated ) and ( ClientSocket.Connected ) do
      try
        // В это место обычно помещается код,
        // ожидающий входных данных, читающий из сокета или пишущий в него
        // Пример, приведённый ниже, показывает, что можно добавить в данную
        // секцию программы.
      except on e:exception do
        begin
          // Если произошла ошибка, то закрываем сокет и выходим
          ClientSocket.Close;
          Terminate;
        end;
      end;
    finally
      fSocketStream.Free;
    end;
  except on e:exception do
    begin
      // Если произошла ошибка, то закрываем сокет и выходим
      ClientSocket.Close;
      Terminate;
    end;
  end;
end;

Когда связь установлена, потоку необходимо ожидать входящих данных(запроса от клиента). Для этого можно использовать следующий код:

if ( not Terminated ) and
   ( not fSocketStream.WaitForData( 1000000 ) ) then
begin
  // Обработчик таймаута (т.е. если по истечении 1000000 миллисекунд
  // от клиента не пришло запроса
end;
// В сокете есть входящие данные!

Для чтения данных, Вам понадобится создать буфер для хранения полученных данных. Обычно буфер - это PByteArray или массив символов. В этом примере я обозвал буфер как fRequest который является массивом символов. Кроме того я ожидаю фиксированное количество байт. Массив имеет постоянный размер REQUESTSIZE.

var
  ac, readlen : integer;
begin
  FillChar( fRequest, REQUESTSIZE, 0 );
  ac := 0;
  repeat
    readlen := fSocketStream.Read( fRequest[ac],
                                   1024 );
    // считываем блоки по 1024 байт, до тех пор, пока буфер
    // не заполнится
    ac := ac+readlen;
  until ( readlen = 0 ) or ( ac = REQUESTSIZE );
end;

Если readlen равно 0, значит больше нет входящих данных. Функция Чтения завершается через 100000 миллисекунд после запуска в TWinSocketStream.Create(). Если Вы не знаете сколько времени нужно ожидать запроса от клиента, то чем меньше будет таймаут, тем лучше. В большинстве случаев максимальный таймаут не должен превышать 30 секунд.

При посылке ответа, Вы должны знать, в каком режиме работает клиент. Многие клиенты ожидают только один пакет ответа, другие ожидают несколько пакетов. В этом примере, я подразумеваю клиента, который ожидает только один пакет, так что я должен послать мои данные назад в одном блоке:

fSocketStream.WriteBuffer( fRep, fReplySize );

fRep это буфер, содержащий ответ на запрос клиента, и fReplySize - это размер буфера.