unit Unit2;
interface
uses
Winapi.Windows,
Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
Vcl.StdCtrls, System.Win.ScktComp;
type
TSocketBufferDataEvent =
procedure (Sender: TOBject; Socket: TCustomWinSocket;
const Data: TBytes)
of Object;
TSocketBuffer =
class(TObject)
strict private
FBuffer: TMemoryStream;
FLength: UInt32;
strict private
FOnData: TSocketBufferDataEvent;
public
procedure Receive(Socket: TCustomWinSocket);
public
constructor Create;
destructor Destroy;
override;
public
property OnData: TSocketBufferDataEvent
read FOnData
write FOnData;
end;
TForm2 =
class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
strict private
FClient: TClientSocket;
FClientBuffer: TSocketBuffer;
FServer: TServerSocket;
strict private
procedure Send(Socket: TCustomWinSocket;
const Data: TBytes);
strict private
procedure ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientRead(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientData(Sender: TOBject; Socket: TCustomWinSocket;
const Data: TBytes);
procedure ServerClientConnect(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerRead(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerData(Sender: TOBject; Socket: TCustomWinSocket;
const Data: TBytes);
public
{ Public-Deklarationen }
end;
var
Form2: TForm2;
implementation
uses
System.Math;
{$R *.dfm}
{ TSocketBuffer }
constructor TSocketBuffer.Create;
begin
inherited Create;
FBuffer := TMemoryStream.Create;
FLength := 0;
end;
destructor TSocketBuffer.Destroy;
begin
FBuffer.Free;
inherited;
end;
procedure TSocketBuffer.Receive(Socket: TCustomWinSocket);
type
PUInt32 = ^UInt32;
var
B:
array[0..64*1024 - 1]
of Byte;
N: UInt32;
D: TBytes;
begin
// Erstmal alle Daten in den Buffer schreiben
while (Socket.ReceiveLength > 0)
do
begin
N := System.Math.Min(Socket.ReceiveLength, Length(B));
Socket.ReceiveBuf(B[0], N);
FBuffer.
Write(B[0], N);
Inc(FLength, N);
end;
// Pakete parsen
while (FLength >= SizeOf(N))
do
begin
N := PUInt32(FBuffer.Memory)^;
if (FLength < SizeOf(N) + N)
then
begin
Break;
end;
// Mindestens ein Paket vollständig angekommen
SetLength(D, N);
CopyMemory(@D[0], PByte(FBuffer.Memory) + SizeOf(N), N);
if Assigned(FOnData)
then
begin
FOnData(Self, Socket, D);
end;
// Jetzt muss das bearbeitete Paket aus dem Buffer "entfernt" werden
CopyMemory(FBuffer.Memory, PByte(FBuffer.Memory) + SizeOf(N) + N, FLength - SizeOf(N) - N);
Dec(FLength, SizeOf(N) + N);
FBuffer.Position := FBuffer.Position - SizeOf(N) - N;
end;
end;
{ TForm2 }
procedure TForm2.Button1Click(Sender: TObject);
begin
FClient.Active := true;
end;
procedure TForm2.ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
Send(Socket, TEncoding.UTF8.GetBytes('
This is a test string'));
end;
procedure TForm2.ClientData(Sender: TOBject; Socket: TCustomWinSocket;
const Data: TBytes);
begin
// Vollständiges Paket vom Server empfangen
end;
procedure TForm2.ClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin
FClientBuffer.Receive(Socket);
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
FClient := TClientSocket.Create(Self);
FClient.ClientType := ctNonBlocking;
FClient.Host := '
localhost';
FClient.Port := 12345;
FClient.OnConnect := ClientConnect;
FClient.OnRead := ClientRead;
FClientBuffer := TSocketBuffer.Create;
FClientBuffer.OnData := ClientData;
FServer := TServerSocket.Create(Self);
FServer.ServerType := stNonBlocking;
FServer.Port := 12345;
FServer.OnClientConnect := ServerClientConnect;
FServer.OnClientDisconnect := ServerClientDisconnect;
FServer.OnClientRead := ServerRead;
FServer.Active := true;
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
FClientBuffer.Free;
end;
procedure TForm2.Send(Socket: TCustomWinSocket;
const Data: TBytes);
var
N, S: UInt32;
begin
N := Length(Data);
S := 0;
Assert(Socket.SendBuf(N, SizeOf(N)) = 4);
while (S < N)
do
begin
Inc(S, Socket.SendBuf(Data[S], N - S));
end;
end;
procedure TForm2.ServerClientConnect(Sender: TObject; Socket: TCustomWinSocket);
var
Buffer: TSocketBuffer;
begin
Buffer := TSocketBuffer.Create;
Buffer.OnData := ServerData;
Socket.Data := Buffer;
end;
procedure TForm2.ServerClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
TSocketBuffer(Socket.Data).Free;
end;
procedure TForm2.ServerData(Sender: TOBject; Socket: TCustomWinSocket;
const Data: TBytes);
begin
// Vollständiges Paket vom Client empfangen
Caption := TEncoding.UTF8.GetString(Data);
end;
procedure TForm2.ServerRead(Sender: TObject; Socket: TCustomWinSocket);
begin
TSocketBuffer(Socket.Data).Receive(Socket);
end;
end.