Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Socket C&S (https://www.delphipraxis.net/195149-socket-c-s.html)

tomkupitz 6. Feb 2018 16:48

Socket C&S
 
Hallo,

ich schicke mit

Code:
SendBuf(buf[0], Length(buf));
Daten an den Client. Dort kommen die Daten

Code:
Socket.ReceiveBuf(buf[0], Socket.ReceiveLength);
aber nicht in einem Stück an. Wie groß darf buf max. sein? Oder kann erzwingen das alles komlett empfangen werden kann?

Danke und beste Grüße

Zacherl 6. Feb 2018 16:53

AW: Socket C&S
 
Zitat:

Zitat von tomkupitz (Beitrag 1393230)
aber nicht in einem Stück an. Wie groß darf buf max. sein? Oder kann erzwingen das alles komlett empfangen werden kann?

Kurze und schmerzhafte Antwort: Nein, erzwingen kannst du leider nichts.

Du musst dir ein eigenes kleines Protokoll bauen. Einfachste Lösung wäre das Vorwegsenden eines Ints/Int64 mit der Länge der Daten.

Edit:
Auf Empfängerseite sind leider einige Dinge zu beachten. Z.b. kann es nicht nur passieren, dass die Pakete fragmentiert werden, sondern mehrere Pakete können auch "in einem Rutsch" ankommen. Auch kann es dir passieren, dass dein Int/Int64 nicht vollständig ist. Du benötigst also auf jeden Fall einen Zwischenspeicher (sowas wie eine Queue [FIFO] für zusammenhängende Daten) und musst Diesen dann in einer Schleife stückweise abarbeiten.

Redeemer 6. Feb 2018 19:40

AW: Socket C&S
 
MTU von Ethernet ist normalweise 1518 minus
  • Ethernet (18 Bytes), daher wird die MTU meist mit 1500 angegeben
  • Internet Protocol (v4: meist 20 Bytes; v6: 40 Bytes)
  • Transport (UDP: 8 Bytes; TCP: 20 Bytes)
Darauf verlassen kann man sich wie Zacherl schon sagte aber nicht, da der Nutzer das einstellen kann.
Indy macht aber auch manchmal Blödsinn. Ich habe auch schon mal das Problem gehabt, dass bestimmte Indy-Methoden das erste Byte einzeln geschickt haben.

tomkupitz 6. Feb 2018 20:15

AW: Socket C&S
 
Hallo,

wenn die Daten blockweise eintreffen sollte ReceiveLength die jeweilige Blockgröße angeben. Wie ich aber lese ist der Wert von ReceiveLength nicht immer genau. Damit wird es unmöglich die gesamten Daten "zusammenzupacken".

Oder hat jemand doch ein Stück Quelltext?

Beste Grüße

Redeemer 6. Feb 2018 21:45

AW: Socket C&S
 
Irgendwie so (nicht getestet):
Delphi-Quellcode:
var
  buf: TBytes;
  len: Integer;
begin
  // bla bla
  len := Length(buf);
  SendBuf(len, 4);
  SendBuf(buf[0], Length(buf));
end;
Delphi-Quellcode:
var
  buf: TBytes;
  len: Integer;
begin
  Socket.ReceiveBuf(len, 4);
  SetLength(buf, len);
  Socket.ReceiveBuf(buf[0], len);
  // bla bla
end;

Zacherl 6. Feb 2018 21:47

AW: Socket C&S
 
Delphi-Quellcode:
ReceiveLength
enthält die Größe der ankommenden Daten. Aber wie du schon korrekt erkannt hast, wird ein
Delphi-Quellcode:
Send
manchmal in mehrere
Delphi-Quellcode:
Recv
s aufgeteilt sozusagen. Die Summe aller
Delphi-Quellcode:
ReceiveLength
s entspricht dann der bei
Delphi-Quellcode:
Send
angegebenen Gesamtgröße.

Der Trick ist, dass du die Größe manuell einmal mitschickst, wie ich oben schon beschrieben habe.

Zitat:

Zitat von Redeemer (Beitrag 1393248)
Irgendwie so (nicht getestet)

Von der Idee her ja, allerdings müssen die Besonderheiten beachtet werden, die ich oben beschrieben habe. Also man benötigt auf jeden Fall eine Schleife, da auch mehrere Pakete "in einem Rutsch" ankommen können. Ich gucke gleich mal, ob ich noch einen alten Beispielcode finde.

Zacherl 6. Feb 2018 23:00

AW: Socket C&S
 
Dieses Beispiel solltest du 1 zu 1 auf Sockets übertragen können:
Delphi-Quellcode:
type
  TForm1 = class(TForm)
    ...
  strict private
    FBuffer: TMemoryStream;
    FBufferLen: UInt32;

...

procedure TForm1.Recv(const Data: TBytes);
type
  PUInt32 = ^UInt32;
var
  N: UInt32;
  S: String;
begin
  // Erstmal alle Daten in den Buffer schreiben
  FBuffer.Write(Data, Length(Data));
  Inc(FBufferLen, Length(Data));

  // Pakete parsen
  while (FBufferLen >= SizeOf(N)) do
  begin
    N := PUInt32(FBuffer.Memory)^;
    if (FBufferLen < SizeOf(N) + N) then
    begin
      Break;
    end;
    // Mindestens ein Paket vollständig angekommen
    SetLength(S, N div SizeOf(Char));
    CopyMemory(@S[1], PByte(FBuffer.Memory) + SizeOf(N), N);
    ShowMessage(S);

    // Jetzt muss das bearbeitete Paket aus dem Buffer "entfernt" werden
    CopyMemory(FBuffer.Memory, PByte(FBuffer.Memory) + SizeOf(N) + N,
      FBufferLen - SizeOf(N) - N);
    Dec(FBufferLen, SizeOf(N) + N);
    FBuffer.Position := FBuffer.Position - SizeOf(N) - N;
  end;
end;
Hier noch zwei Testvektoren:
Delphi-Quellcode:
procedure TForm1.TestFragmented;
const
  S = 'Dieser String wird mehreren Paketen gesendet';
  X = 40;
var
  N: UInt32;
  D, F1, F2: TBytes;
begin
  N := Length(S) * SizeOf(Char);
  SetLength(D, SizeOf(N) + N);
  Move(N, D[0], SizeOf(N));
  Move(S[1], D[SizeOf(N)], N);
  SetLength(F1, X);
  Move(D[0], F1[0], X);
  SetLength(F2, Length(D) - X);
  Move(D[X], F2[0], Length(D) - X);
  // Einzelnes Paket wird fragmentiert empfangen
  Recv(F1);
  Recv(F2);
end;

procedure TForm1.TestMultiple;
const
  S1 = 'Dies ist String 1';
  S2 = 'Dies ist String 2';
var
  N1, N2: UInt32;
  D: TBytes;
begin
  N1 := Length(S1) * SizeOf(Char);
  N2 := Length(S2) * SizeOf(Char);
  SetLength(D, SizeOf(N1) + N1 + SizeOf(N2) + N2);
  Move(N1, D[0], SizeOf(N1));
  Move(S1[1], D[SizeOf(N1)], N1);
  Move(N2, D[SizeOf(N1) + N1], SizeOf(N2));
  Move(S2[1], D[SizeOf(N1) + N1 + SizeOf(N2)], N2);
  // Zwei Pakete werden als einzelnes Paket empfangen
  Recv(D);
end
Ich verwende hier absichtlich das extra Feld
Delphi-Quellcode:
FBufferLen
statt direkt
Delphi-Quellcode:
FBuffer.Size
, um frequente Speicher-Allocs zu vermeiden.

tomkupitz 7. Feb 2018 10:55

AW: Socket C&S
 
Puffer zusammenpacken ist an sich kein Problem. Nur der Bezug zum Socket war mir wichtig.

Es sieht so aus, als wäre die max. Größe eines Blocks ein High(Word). Ich werde versuchen schon beim Versenden diese max. Blockgröße nicht zu überscheiten.

stahli 7. Feb 2018 11:18

AW: Socket C&S
 
Vielleicht hilft Dir der Thread noch etwas weiter: http://www.delphipraxis.net/190482-s...ockettest.html

Zacherl 7. Feb 2018 14:04

AW: Socket C&S
 
Zitat:

Zitat von tomkupitz (Beitrag 1393281)
Es sieht so aus, als wäre die max. Größe eines Blocks ein High(Word). Ich werde versuchen schon beim Versenden diese max. Blockgröße nicht zu überscheiten.

Versuch dein Glück, wenn du auf Redeemer und mich nicht hören willst, aber erwarte nicht, dass es zuverlässig funktioniert.

Die 65k bzw. mitlerweile meisten 256k, von denen man öfters mal liest, beziehen sich auf die Größe des internen Empfangs-Buffers unter Windows und nicht auf die MTU. Die EINZIGE zuverlässige Methode bei TCP ist eine eigene Pakettrennung. Denn selbst, wenn du es schaffen würdest dich immer korrekt an der MTU zu orientieren, dann schützt dich das weiterhin nicht davor, dass mehrere kleine Pakete zu einem großen Datenblock auf Empfängerseite zusammengefügt werden.

Mein Beispiel verwendet zwar keine Sockets, simuliert aber beide der primären Situationen, die beim Versand von Daten per TCP Socket auftreten und ist demnach absolut das, was du haben möchtest. Die
Delphi-Quellcode:
Recv
Methode kannst du praktisch 1 zu 1 übernehmen und mit den Daten des Sockets füllen und beim
Delphi-Quellcode:
Send
schickst du halt schnell einen
Delphi-Quellcode:
UInt32
mit der Gesamtdatenmenge des Pakets vorweg. Ist kein großer Aufwand.

tomkupitz 8. Feb 2018 19:47

AW: Socket C&S
 
Zitat:

Versuch dein Glück, wenn du auf Redeemer und mich nicht hören willst, aber erwarte nicht, dass es zuverlässig funktioniert.
Alles gut. Ich habe eure Beispiele probiert. Danke dafür.

Ergebnis meiner Tests: Es scheint ein Zeitproblem zu geben. Baue ich eine Pause nach jedem

Code:
if ServerSocket.Socket.ActiveConnections>0 then
  ServerSocket.Socket.Connections[0].SendBuf(...);
ein kommt auch alles an.

Zacherl 8. Feb 2018 23:02

AW: Socket C&S
 
Zitat:

Zitat von tomkupitz (Beitrag 1393404)
Ergebnis meiner Tests: Es scheint ein Zeitproblem zu geben. Baue ich eine Pause nach jedem

Sorry, aber ... :roll: Die "Pause" löst dein Problem in keinster Weise.

himitsu 9. Feb 2018 09:32

AW: Socket C&S
 
Zitat:

Zitat von Zacherl (Beitrag 1393409)
Zitat:

Zitat von tomkupitz (Beitrag 1393404)
Ergebnis meiner Tests: Es scheint ein Zeitproblem zu geben. Baue ich eine Pause nach jedem

Sorry, aber ... :roll: Die "Pause" löst dein Problem in keinster Weise.

Ist das Netz langsamer als deine Pause, dann mußt du die Pause weiter vergrößern, sonst knallt es wieder.
Und hängt es mal noch mehr, ...


Besser du baust sicherheitshalber gleich ein Sleep(3600000); ein.

tomkupitz 9. Feb 2018 12:45

AW: Socket C&S
 
Zitat:

Sorry, aber ... Die "Pause" löst dein Problem in keinster Weise.
Genau so sehe ich das auch. Bisher habe ich aber noch keine "saubere" Übertragung für meinen Kontext gefunden.

jfheins 9. Feb 2018 12:55

AW: Socket C&S
 
TCP ist immer ein "Stream", das heißt die Daten können schlimmstenfalls byteweise ankommen. (Bytes kommen aber immer ganz oder gar nicht an, darauf kann man sich zum Glück verlassen ;-) )

Ich habe das damals so gelöst, dass ich einen struct/record mit den Nutzdaten verschickt habe (konstante Länge). Auf Empfangsseite habe ich einen passend dimensionierten Puffer vorgehalten. Immer, wenn was angekommen ist, habe ich die Daten (in einer Schleife, solange neue Daten da waren!) ein Byte nach links geschoben, ein Byte neue Daten hinten angehängt und geprüft ob das ein gültiges Paket ist. (Konstante prüfen plus Checksumme)
Bei einem gültigen Paket wurde ein Callback ausgelöst.

Du kannst auch immer einen Längenwert vor die Daten schreiben. Oder ein Trennzeichen (Zeilenumbruch) zwischen die Daten. Du brauchst irgendein Verfahren, um deine Daten in einen kontinuierlichen Stream zu schreiben und (kniffeliger) sie aus einem kontinuierlichen Stream wieder herauszufischen.

Ich bin mir nicht sicher, was du mit "sauberer Übertragung" meinst. Um noch deine ursprüngliche Frage zu beantworten:
Zitat:

Wie groß darf buf max. sein? Oder kann erzwingen das alles komplett empfangen werden kann?
Ein Byte. Alles über einem Byte kann zerstückelt ankommen.

stahli 9. Feb 2018 13:00

AW: Socket C&S
 
Du kannst halt nie sicher sagen, wann welche Paketteile auf der anderen Seite ankommen.

Wenn Du etwa sendest:
---
Dies
ist
Paket
1
---
Jetzt
kommt
Paket
2
---

kann auf der anderen Seite ankommen:
---
Dies
ist
Paket
1
Jetzt
kom
---
mt
Paket
2
---

oder alles in einem Block oder in 3 Blöcken.

Du musst also die Eingangsdaten puffern und selbst ermitteln, ob ein vollständiges Paket vorliegt, welches Du verarbeiten kannst.
Dabei können aber auch schon Daten für 2 und 1/4 weitere Pakete im Puffer vorliegen.

Dabei ist auch noch zu beachten, dass Daten von unterschiedlichen Clients reinkommen können. Also brauchst Du den o.g. Puffer ggf. pro Client.

tomkupitz 9. Feb 2018 13:26

AW: Socket C&S
 
Schön wäre wenn immer alles ankommen würde. Ich habe n * 1024 Bytes geschickt (gesamt ca. 200000 Bytes). Angekommen sind ca. 65000 Bytes. Immer! Nur mit Pause beim Senden hat es funktioniert.

jfheins 9. Feb 2018 14:34

AW: Socket C&S
 
Moin,
was benutzt du eigentlich genau für Komponenten und Einstellungen? Beispielsweise steht in der Hilfe zu TCustomWinSocket.SendBuf:
Zitat:

Bei nicht-blockierenden Sockets werden die Daten an die WinSock DLL gesendet, die über eigene interne Puffer verfügt. Wenn WinSock weitere Daten akzeptieren kann, gibt SendBuf sofort die Anzahl der Byte in der Warteschlange zurück. Wenn der interne Puffer von WinSock den gesendeten Puffer nicht mehr aufnehmen kann, gibt SendBuf -1 zurück, und es werden keine Daten in die Warteschlange übernommen. In diesem Fall müssen Sie etwas warten, bis WinSock die sich bereits in der Warteschlange befindlichen Daten gesendet hat und es dann erneut versuchen.

Bei nicht-blockierenden Sockets gibt SendBuf die Anzahl der tatsächlich geschriebenen Bytes zurück.
Du musst also eigentlich den Rückgabewert prüfen und dann ggf. eine Pause einlegen.

Zacherl 9. Feb 2018 16:07

AW: Socket C&S
 
Zitat:

Zitat von tomkupitz (Beitrag 1393444)
Zitat:

Sorry, aber ... Die "Pause" löst dein Problem in keinster Weise.
Genau so sehe ich das auch. Bisher habe ich aber noch keine "saubere" Übertragung für meinen Kontext gefunden.

Eine Möglichkeit (Längenprefix) - die du wirklich 1 zu 1 übernehmen kannst - habe ich dir doch schon gegeben. Die weiteren Varianten (feste Größe pro "Paket" und Trennzeichen) hat jfheins dir jetzt auch genannt. Alle diese Verfahren sind absolut sauber und bieten keinen Spielraum für inkorrekt übertragene Daten (sofern korrekt implementiert natürlich). Welches Verfahren man nimmt, ist Geschmackssache. Trennzeichen sind problematisch bei Binärdaten und durch Pakete mit fester Größe hat man natürlich Probleme bei dynamischen Daten z.b. String mit variabler Länge, etc. Deshalb bevorzuge ich das Längenprefix.

tomkupitz 9. Feb 2018 16:52

AW: Socket C&S
 
Sooo...

Server:

Code:
procedure TForm1.Button1Click(Sender: TObject);
var bmp: TBitmap;
    buf: array of Byte;
    res, len, bufidx, bufsize: Integer;

    procedure BmpToBuf;
    var i, x, y, w, h: Integer;
        p: PByteArray;

    begin
      w:=bmp.Width;
      h:=bmp.Height;

      bmp.PixelFormat:=pf24bit;

      i:=1234;

      Move(i, buf[0], sizeof(Integer));

      Move(w, buf[sizeof(Integer)], sizeof(Integer));
      Move(h, buf[2*sizeof(Integer)], sizeof(Integer));

      i:=sizeof(Integer)+2*sizeof(Integer);

      for y:=0 to h-1 do
      begin
        p:=bmp.Scanline[y];
        Move(p[0], buf[i], w*3);
        inc(i, w*3);
      end;
    end;

begin
  bmp:=<MacheinScreenshotvomFenster>(Handle);

  len:=sizeof(Integer)+2*sizeof(Integer)+bmp.Height*bmp.Width*3;

  if len mod 1024 <> 0 then
    inc(len, 1024-(len mod 1024));

  SetLength(buf, len);
  BmpToBuf;

  bmp.Free;

  if ServerSocket.Socket.ActiveConnections>0 then
  begin
    bufidx:=0; bufsize:=Length(buf);

    while bufsize>0 do
    begin
      repeat
        res:=ServerSocket.Socket.Connections[0].SendBuf(buf[bufidx], Min(1024, bufsize));
        if res<>1024 then <MacheinePause>(50);
      until res<>-1;

      inc(bufidx, Min(1024, bufsize));
      dec(bufsize, Min(1024, bufsize));
    end;
  end;

  Finalize(buf);
end;
Client:

Code:
var
  buf: array of Byte;
  sz: Integer = -1;

  id, wi, hi: Integer;

procedure TForm1.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var bmp: TBitmap;
    len, res: Integer;

    procedure BufToBmp;
    var i, x, y, w, h: Integer;
        p: PByteArray;

    begin
      bmp:=TBitmap.Create;
      bmp.PixelFormat:=pf24Bit;

      Move(buf[0], i, sizeof(Integer));

      Move(buf[sizeof(Integer)], w, sizeof(Integer));
      Move(buf[2*sizeof(Integer)], h, sizeof(Integer));

      bmp.Width:=w;
      bmp.Height:=h;

      i:=sizeof(Integer)+2*sizeof(Integer);

      for y:=0 to h-1 do
      begin
        if i+w*3-1>Length(buf) then
          Break;

        p:=bmp.Scanline[y];
        Move(buf[i], p[0], w*3);
        inc(i, w*3);
      end;
    end;

begin
  len:=Length(buf);

  SetLength(buf, len+1024);

  res:=Socket.ReceiveBuf(buf[len], 1024);

  if (sz=-1) and (Length(buf)>=sizeof(Integer)+2*sizeof(Integer)) then
  begin
    Move(buf[0], id, sizeof(Integer));

    Move(buf[sizeof(Integer)], wi, sizeof(Integer));
    Move(buf[2*sizeof(Integer)], hi, sizeof(Integer));

    if id=1234 then
    begin
      sz:=sizeof(Integer)+2*sizeof(Integer)+hi*wi*3;

      if sz mod 1024 <> 0 then
        inc(sz, 1024-(sz mod 1024));
    end;
  end;

  if (sz>-1) and (Length(buf)=sz) then
  begin
    BufToBmp;
    Image1.Picture.Bitmap.Assign(bmp);
    bmp.Free;

    Finalize(buf);
    sz:=-1;
  end;
end;

Zacherl 9. Feb 2018 20:24

AW: Socket C&S
 
Also tatsächlich nonblocking Sockets?

Eine Sache, die mir grade noch auffällt, ist das Speichern/Laden des Bitmaps. Gibt es einen Grund, warum du nicht z.b.
Delphi-Quellcode:
TMemoryStream
verwendest? Dann reduziert sich dein kompletter Code mit den
Delphi-Quellcode:
ScanLine
s nämlich auf ein
Delphi-Quellcode:
I := 1234;
Stream.Write(I);
Bitmap.SaveToStream(Stream)
analog dazu das Laden:
Delphi-Quellcode:
Stream.Read(I);
if (I = 1234) then
  Bitmap.LoadFromStream(Stream)

scrat1979 9. Feb 2018 20:37

AW: Socket C&S
 
Ich kann dir die sgcWebSockets nur wärmstens empfehlen. Damit habe ich mir innerhalb von paar Stunden meinen eigenen Server und Client abgeleitet und jetzt wird er für einen netzwerkfähigen Terminkalender verwendet. Einfach klasse!

tomkupitz 9. Feb 2018 21:07

AW: Socket C&S
 
Zitat:

Also tatsächlich nonblocking Sockets?
ja, nonblocking.

Zitat:

Gibt es einen Grund, warum du nicht z.b. TMemoryStream verwendest?
nein, habe ich nur zu Testzwecken verwendet, ist also noch nicht "optimiert".

Zacherl 9. Feb 2018 23:31

AW: Socket C&S
 
So in etwa würde ich vorgehen, um direkt nach dem Connecten einen simplen String an den Server zu schicken (btw. die Socket Komponenten sind schon sehr lange deprecated und evtl. könnten blocking Sockets hier für dich auch besser geeignet sein):
Delphi-Quellcode:
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.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:37 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz