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