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

Прием текста, передаваемого с помощью метода SendText

01.01.2007
{ **** UBPFD *********** by kladovka.net.ru ****
>> Приём и обработка пакетов переданных методом SendText() - с учётом "склеенных" и полученных неполностью пакетов.
 
Юнит RecvPckt предназначен для приёма текста, передаваемого с помощью метода SendText
объекта Socket:TCustomWinSocket. Данный юнит может использоваться как клиентом так
и сервером для обработки принятого пакета.
 
Функции юнита предусматривают возможность получения "склеенных" пакетов,
или пакетов, пришедших не полностью.
 
Тип TBuffer;
FBuffer - хранит в себе принимаемый пакет
FCurrentPacketSize = хранит сведения о полной длине принимаемого пакета.
 
Описание функций и процедур, необходимых для использования в других юнитах
 
Procedure ClearBuffer(var ABuffer:TBuffer);
Очищает буффер FBuffer и обнуляет значение FCurrentPacketSize;
 
Function ProcessReceivedPacket(var ABuffer:TBuffer; var APacket:String):Boolean;
В данную функцию передаётся полученный от клиента/сервера пакет, через аргумент APacket
Принцип работы этой функции заключается в накоплении получаемого текста в поле
FBuffer объекта ABuffer. В случае когда FBuffer будет содержать полностью весь пакет,
функция возвратит True, иначе возвращает False
 
Функция ОТПРАВКИ текста:
 
Function SendTextToSocket(Socket:TCustomWinSocket; const Text:String):Integer;
begin
Result := -1;
IF
Text = '' then exit;
IF
Socket.Connected then
Result := Socket.SendText(IntToStr(Length(Text))+'#'+Text);
end;
 
Зависимости: sysutils
Автор:       VID, snap@iwt.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        30 сентября 2002 г.
********************************************** }
 
unit
RecvPckt;
 
interface
 
uses
 
SysUtils;
 
Type
 
TReadHeaderResult = record
   
FPacketSize:Integer;
   
FPacketSizeStr:String;
   
FTextStartsAt:Integer;
 
end;
 
 
TBuffer = record
   
FBuffer:String;
   
FHeaderBuffer:String;
   
FCurrentPacketSize:Integer;
 
end;
 
 
Procedure ClearBuffer(var ABuffer:TBuffer);
 
Function ReadHeader(var ABuffer:TBuffer; var APacket:String):TReadHeaderResult;
 
Function ProcessReceivedPacket(var ABuffer:TBuffer; var APacket:String):Boolean;
 
implementation
 
Procedure ClearBuffer(var ABuffer:TBuffer);
begin
 
With ABuffer do
 
begin
   
FBuffer := '';
   
FHeaderBuffer := '';
   
FCurrentPacketSize := 0;
 
end;
end;
 
Function ReadHeader(var ABuffer:TBuffer; var APacket:String):TReadHeaderResult;
Var X:Integer;
 
 
Procedure ClearHeader;
 
begin
   
ABuffer.FHeaderBuffer := '';
 
end;
 
 
Function CorrectPacket:Boolean;
 
Var I,L:Integer;
 
begin
    X
:=0; L:=Length(APacket);
    FOR I
:=1 TO L DO
      IF
(APacket[I] in ['0'..'9']) then Break
     
else
        IF
(APacket[I]='#') and (ABuffer.FHeaderBuffer<>'') then Break
       
else X:=I;
    IF X
>0 then Delete(APacket, 1, X);
   
Result := APacket <> '';
 
end;
 
 
Procedure GetHeader;
 
Var I,L:Integer;
 
begin
    L
:=Length(APacket); X:=0;
    FOR I
:=1 TO L DO
   
begin
      X
:=I;
      IF
(APacket[I] in ['0'..'9']) then
     
begin
       
Insert(APacket[I], ABuffer.FHeaderBuffer, Length(ABuffer.FHeaderBuffer)+1);
     
end else Break;
   
end;
 
end;
 
 
Procedure SetResultToNone;
 
begin
   
With Result do
   
begin
     
FPacketSize := 0;
     
FTextStartsAt := 0;
     
FPacketSizeStr := '';
   
end;
 
end;
 
begin
 
SetResultToNone;
  IF
APacket = '' then Exit;
  IF
ABuffer.FCurrentPacketSize > 0 then
 
begin
   
With Result do
   
begin
     
FPacketSize := ABuffer.FCurrentPacketSize;
     
FPacketSizeStr := IntToStr(ABuffer.FCurrentPacketSize);
     
FTextStartsAt := 1;
   
end;
   
Exit;
 
end;
  IF
not CorrectPacket then Exit;
 
GetHeader;
  IF
APacket[X]='#' then
 
begin
   
Inc(X);
   
Try
     
Result.FPacketSize := StrToInt(ABuffer.FHeaderBuffer);
   
except end;
   
Result.FPacketSizeStr := ABuffer.FHeaderBuffer; ClearHeader;
 
end else
    IF
not (APacket[X] in ['0'..'9']) then ClearHeader;
 
Result.FTextStartsAt := X;
end;
 
Function ProcessReceivedPacket(var ABuffer:TBuffer; var APacket:String):Boolean;
Var ReadHeaderResult:TReadHeaderResult;
   
NeedToCopy, DelSize:Integer;
    S
:String;
 
   
Function FullPacket:Boolean;
   
begin
     
With ABuffer do Result := Length(FBuffer) = FCurrentPacketSize;
   
end;
 
begin
 
Result := True;
  IF
APacket = '' then Exit;
  IF
ABuffer.FBuffer = '' then
 
begin
   
ReadHeaderResult := ReadHeader(ABuffer, APacket);
   
ABuffer.FCurrentPacketSize := ReadHeaderResult.FPacketSize;
    S
:=Copy(APacket, ReadHeaderResult.FTextStartsAt, ReadHeaderResult.FPacketSize);
   
DelSize := Length(ReadHeaderResult.FPacketSizeStr)+ReadHeaderResult.FPacketSize+1;
 
end else
 
begin
   
With ABuffer do NeedToCopy := FCurrentPacketSize - Length(FBuffer);
    S
:=Copy(APacket, 1, NeedToCopy);
   
DelSize := NeedToCopy;
 
end;
 
With ABuffer do
    IF
FCurrentPacketSize > 0 then Insert(S, FBuffer, Length(FBuffer)+1);
  IF
not FullPacket then Result := False;
  IF
ABuffer.FHeaderBuffer = '' then
   
Delete(APacket, 1, DelSize)
 
else begin APacket := ''; Result := False; end;
end;
 
end.

Пример использования:

Var GBuffer:TBuffer; //Объявляем переменную типа TBuffer. Для каждого клиента на сервере должна быть объявлена отдельная переменная этого типа
...
 
procedure TForm1
.ServerClientRead(Sender: TObject;
 
Socket: TCustomWinSocket);
VAR S
:String;
begin
  S
:=Socket.ReceiveText;
  REPEAT
    IF
ProcessReceivedPacket(GBuffer, S) then
      IF
GBuffer.FBuffer <> '' then
       
try
         
Recv.Lines.Add(GBuffer.FBuffer);
         
//Или же передать GBuffer.FBuffer на исполнение.
       
finally
         
ClearBuffer(GBuffer);
       
end;
  UNTIL S
='';
end