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