![]() |
Socket C&S
Hallo,
ich schicke mit
Code:
Daten an den Client. Dort kommen die Daten
SendBuf(buf[0], Length(buf));
Code:
aber nicht in einem Stück an. Wie groß darf buf max. sein? Oder kann erzwingen das alles komlett empfangen werden kann?
Socket.ReceiveBuf(buf[0], Socket.ReceiveLength);
Danke und beste Grüße |
AW: Socket C&S
Zitat:
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. |
AW: Socket C&S
MTU von Ethernet ist normalweise 1518 minus
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. |
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 |
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; |
AW: Socket C&S
Delphi-Quellcode:
enthält die Größe der ankommenden Daten. Aber wie du schon korrekt erkannt hast, wird ein
ReceiveLength
Delphi-Quellcode:
manchmal in mehrere
Send
Delphi-Quellcode:
s aufgeteilt sozusagen. Die Summe aller
Recv
Delphi-Quellcode:
s entspricht dann der bei
ReceiveLength
Delphi-Quellcode:
angegebenen Gesamtgröße.
Send
Der Trick ist, dass du die Größe manuell einmal mitschickst, wie ich oben schon beschrieben habe. Zitat:
|
AW: Socket C&S
Dieses Beispiel solltest du 1 zu 1 auf Sockets übertragen können:
Delphi-Quellcode:
Hier noch zwei Testvektoren:
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;
Delphi-Quellcode:
Ich verwende hier absichtlich das extra Feld
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
Delphi-Quellcode:
statt direkt
FBufferLen
Delphi-Quellcode:
, um frequente Speicher-Allocs zu vermeiden.
FBuffer.Size
|
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. |
AW: Socket C&S
Vielleicht hilft Dir der Thread noch etwas weiter:
![]() |
AW: Socket C&S
Zitat:
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:
Methode kannst du praktisch 1 zu 1 übernehmen und mit den Daten des Sockets füllen und beim
Recv
Delphi-Quellcode:
schickst du halt schnell einen
Send
Delphi-Quellcode:
mit der Gesamtdatenmenge des Pakets vorweg. Ist kein großer Aufwand.
UInt32
|
AW: Socket C&S
Zitat:
Ergebnis meiner Tests: Es scheint ein Zeitproblem zu geben. Baue ich eine Pause nach jedem
Code:
ein kommt auch alles an.
if ServerSocket.Socket.ActiveConnections>0 then
ServerSocket.Socket.Connections[0].SendBuf(...); |
AW: Socket C&S
Zitat:
|
AW: Socket C&S
Zitat:
Und hängt es mal noch mehr, ... Besser du baust sicherheitshalber gleich ein Sleep(3600000); ein. |
AW: Socket C&S
Zitat:
|
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:
|
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. |
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.
|
AW: Socket C&S
Moin,
was benutzt du eigentlich genau für Komponenten und Einstellungen? Beispielsweise steht in der ![]() Zitat:
|
AW: Socket C&S
Zitat:
|
AW: Socket C&S
Sooo...
Server:
Code:
Client:
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;
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; |
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:
verwendest? Dann reduziert sich dein kompletter Code mit den
TMemoryStream
Delphi-Quellcode:
s nämlich auf ein
ScanLine
Delphi-Quellcode:
analog dazu das Laden:
I := 1234;
Stream.Write(I); Bitmap.SaveToStream(Stream)
Delphi-Quellcode:
Stream.Read(I);
if (I = 1234) then Bitmap.LoadFromStream(Stream) |
AW: Socket C&S
Ich kann dir die
![]() |
AW: Socket C&S
Zitat:
Zitat:
|
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 01:36 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz