Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

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

01.01.2007

Автор: 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 - это размер буфера.

Взято из https://forum.sources.ru