kleine Frage zu Strings (lNet)

Für Fragen von Einsteigern und Programmieranfängern...
MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

kleine Frage zu Strings (lNet)

Beitrag von MAC »

Hallo.
Um über TCP/IP zu kommunizieren habe ich bisher erst die aktuelle Nachricht (z.B. 1 kb) in einen String gespeichert und dann an die gesammtnachricht (z.B. 200 MB angehängt.) da die kommunikation ja immer in kleinen Packeten geschieht.
Da gibt es natürlich Verbesserungspotential, da wenn man mal 200 MB an Daten verschickt viel im Speicher rumgeworfen wird.
Da die Funktion aSocket.GetMessage(out astring:string) aber nur aus 4 Zeilen besteht , wolte ich eine Eigene Funktion erstellen die die Nachricht direkt zum String anhängt:

Code: Alles auswählen

// ORIGINAL
function TLSocket.GetMessage(out msg: string): Integer;
begin
  Result := 0;
  SetLength(msg, BUFFER_SIZE); // Buffersize ist eine Constante = 262144
  SetLength(msg, Get(PChar(msg)^, Length(msg)));
  Result := Length(msg);
end;
 
// Meine Version:
procedure DownloadMsg(aSocket: TLSocket);
var
   a:integer;
begin
a := length(buffer); // buffer ist hier der string in dem gespeichert wird.
SetLength(buffer, a+1+lCommon.BUFFER_SIZE); // das gleiche Buffersize wie oben ...
SetLength(buffer,a+aSocket.Get(PCHAR(@buffer[a+1])^, lCommon.BUFFER_SIZE));
end;

Ruf ich die Original Funktion auf so funktioniert alles einwandfrei.
Bei der Bearbeiteten Version steht beim Server , welche die Vielen Daten Sendet , nach einiger zeit der Buffer ist voll. Daraus schließe ich das ich hier etwas falsch angegeben habe.

die Get Funktion ist public und so definiert:

Code: Alles auswählen

Get(out aData; const aSize: Integer): Integer; virtual;

Code: Alles auswählen

Signatur := nil;

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: kleine Frage zu Strings (lNet)

Beitrag von Displaced »

// doppeledit
meine güte was schreib ich da eigentlich.. vergiss es falls du es vorher gelesen hast es ist schon zu spät für mich ich guck mir das in ruhe morgen an

Hier mal meine Funktion wie ich das gelöst habe
Im OnReveice event

Code: Alles auswählen

var
  aData: array[0..4095] of byte;
  CheckSize, i: Integer;
begin
...
  repeat
    CheckSize := aSocket.Get(aData[0], 4096); // kann man auch von mir aus höher setzen
    i := length(Buffer);
    SetLength(Buffer, i + CheckSize);
    move(aData[0], Buffer[i], CheckSize);
  until (CheckSize = 0);
...
Vielleicht hilft dir das weiter bis ich deine Funktion durchblickt habe.

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1617
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: kleine Frage zu Strings (lNet)

Beitrag von corpsman »

Wenn ich das richtig sehe, versuchst du mit Lnet große Daten zu versenden. Genau das kann mein Send Get File. Hier werden auch strings und Daten versendet. Evtl hilft dir das weiter.
--
Just try it

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: kleine Frage zu Strings (lNet)

Beitrag von Displaced »

Stimmt ja. Wie sendest du denn deine Daten?

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1617
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: kleine Frage zu Strings (lNet)

Beitrag von corpsman »

so wie es hier beschrieben wurde.

Im Source kannst du das im Event Handler LTCPComponent1CanSend sehen. Der Rest drum rum ist nur das Protokoll beiwerk.

Was ich bisher noch nicht gelöst habe, ist wie man das Portforwarding auf einer der beiden Seiten deaktivieren kann. Wenn du da ne Idee hast, bin ich offen für.
--
Just try it

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: kleine Frage zu Strings (lNet)

Beitrag von Displaced »

Ne ich meinte MAC, dass das mit lNet über das CanSend Event stattfinden sollte weiß ich. Hab selbst derartiges umgesetzt ;)

Was meinst du denn genau mit Portforwarding auf einer Seite deaktivieren? lNet an sich kann ja gar kein Portforwarding. Wenn du nen Listen setzt und kein Forward im Router aktiviert ist, kannste das auch nicht von außen sehen. Es sei denn du versuchst in der Firewall nen Tunnel zu bohren.
Oder meinst du was komplett anderes :?:

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1617
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: kleine Frage zu Strings (lNet)

Beitrag von corpsman »

Ungefähr dass meinte ich.

Mein Problem Aktuell ist, dass einer der Beiden Rechner ein Portforwarding durch den Router braucht. Was bei mir zu Haus kein Problem ist, da habe ich eines. Schöner wäre aber dennoch wenn ich es nicht brüchte. Es gibt einen Ansatz den z.B. Teamviewer verfolgt nur durch den bin ich noch nicht ganz durchgestiegen. Bzw. prinzipiell schon, nur wie man es umsetzt nicht.
--
Just try it

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6766
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: kleine Frage zu Strings (lNet)

Beitrag von af0815 »

Tunneln - zB. OpenVPN.

Teamviewer sucht sich ein einen offenen durch NAT durchgelassenen Port (meist 80) auch bei 'zugenagelten' Systemen und das auf beiden Systemen und setzt sich wenn nötig als Proxy dazwischen.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: kleine Frage zu Strings (lNet)

Beitrag von MAC »

@Displaced : Danke an dein Beilspiel.

Also den Buffer Error, schein ich wohl zu bekommen wenn ich einfach zu große dateien auf einmal sende.
Komisch daran ist halt das das senden (zumindest lokal) funktioniert aber danach keine weitere Datei mehr gesendet Werden kann...

Ich sende meine Datei indem ich sie in nen Eigenes Format packe, das dann als String Exportieren lass und diesen dann sende, so könnte ich eigentlich noch ohne Probleme MetaInformationen aller art beipacken... Gesendet wird das alles an einem Stück (wie der PC das dan letzten Endes aufsplittet hab ich keine Ahnung, ich glaub 25kb stücke...

Ach ja Corpsman, vlt scheint es dir ja hilfreich, aber man muss bei deinem Programm OpenSSL installiert werden (hab ich dann auch gemacht, auch wenn ich kein OpenSSL benutze (hier wärs toll ne möglichkeit per Compiler Schalter zu machen das das einfach nicht mitkompiliert wird so das man wenigstens den Rest schnell testen kann :D )). Zudem habe ich nach erfolgreichem File Transfer ein Error erhalten (welchen kann ich gerade nicht sagen...)

Code: Alles auswählen

Signatur := nil;

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1617
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: kleine Frage zu Strings (lNet)

Beitrag von corpsman »

Installieren musst du OpenSSL nicht, es Reicht die 2 DLL's "libeay32.dll" und "libssl32.dll" in das Verzeichniss der Exe zu kopieren. Unter Linux sint die beiden entsprechenden .so Files standardmäßig vorhanden, deswegen habe ich da keine Probleme. Ich meinte einer meiner Alphatester schrieb auch dass man Admin sein muss damits klappt, aber sicher sagen kann ichs grad nicht.
Ich sende meine Datei indem ich sie in nen Eigenes Format packe, das dann als String Exportieren lass und diesen dann sende, so könnte ich eigentlich noch ohne Probleme MetaInformationen aller art beipacken... Gesendet wird das alles an einem Stück (wie der PC das dan letzten Endes aufsplittet hab ich keine Ahnung, ich glaub 25kb stücke...
Das darfst du nur wenn deine Datei/ zu sendender String kleiner ist wie 65Kb das steht Hier

Der Grund warum du die Übertragung im Netzwerk schaffst und im WAN nicht, ist auch klar. Im LAN sind die Übertragungsraten derart hoch, dass deine Puffer quasi nicht überlaufen können. Meine Anwendung kommt beim Test mit 127.0.0.1 auf ca. 30 MB / Sek, was in etwa der Datenrate meiner Festplatte entspricht. Im 100MBit LAN komme ich auf 7MB/Sek was ebenfalls dem Maximalwert entspricht.

Dein zusätzliches "Verpacken" im eigenen Format halte ich ebenfalls für Kritisch, da du hier deine Dateien unnötig vergrößerst. Das TCP/IP Protokoll stellt eigentlich sicher, dass die Daten ankommen, das sähe bei UDP anders aus. Metadaten würde ich an deiner Stelle getrennt und nur so viele wie nötig übertragen.

Evtl. Beschreibst du uns mal genau, was du eigentlich vorhast, und wie die Daten die du senden willst üblicherweise aussehen.
--
Just try it

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: kleine Frage zu Strings (lNet)

Beitrag von Displaced »

Er macht doch seine Daten nicht größer, jedenfalls nicht dass ich das raus gelesen hätte.
Ich denke was er meint ist sein Protokoll.
Ich hab mir auch ne Bufferklasse geschrieben, die alles in eine Bytearray (in nem string) packt und dann versendet.
Allerdings eben häppchenweise mit dem Event "OnCanSend".
Das wird immer aufgerufen wenn der Socket wieder sendebereit ist =) Das klappt perfekt.

Und zu dem Thema mit dem löchern:
http://www.heise.de/security/artikel/Wi ... 70856.html" onclick="window.open(this.href);return false;
Das ist glaube ich das Prinzip was gemeint ist oder?

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: kleine Frage zu Strings (lNet)

Beitrag von MAC »

So, ich hab mich mal darüber informiert wie man jetzt mit OnCanSend arbeitet.
Generell sieht meine "Konstruktion" dafür so aus (Highlighter Feld1: Wichtiger Code, Highlighter Feld2: Erklärungen)

Code: Alles auswählen

procedure THydroThalamusTCP.fOnCanSend(aSocket: TLSocket);
var
   obj:THTClient;
begin
obj := FindClientServer(aSocket); { Diese Funktion findet meine Eigene Klasse( "THTClient" welche nur einige Variablen enthält) wenn aSocket gegeben ist...}
if assigned(obj) then InternalSend(obj) 
end;      
 
procedure THydroThalamusTCP.InternalSend(obj:THTClient);
var
   a,b:integer;
const
  maxmsg = 65000; // max sendsize, kann reduziert werden, bringt aber nix
begin
obj.cansend := True; // Senden auf True gesetzt
while obj.cansend do
    begin
    a := length(obj.sendbuffer); // länge des Zu sendenen Strings
    if a = 0 then exit; // nix nötig, also byebye
    case a of
    1..maxmsg:b := aTCP.SendMessage(obj.sendbuffer,obj.asocket); { das ist alles in einem Rutsch machbar.}
    else b := aTCP.SendMessage(copy(obj.sendbuffer,1,maxmsg),obj.asocket); {Teilweise Senden}
    end;
    if (b = 0) then
       begin
       obj.cansend := false; // geht nix mehr...
       exit;
       end;
    if b = a then obj.sendbuffer := '' // abziehen was gesendet wurde
               else Delete(obj.sendbuffer,1,b);
    end; // da "While schleife", sendet der solange bis b = 0.
end;
Wenn b = 0 ist, also nicht gesendet werden kann hör ich auf und warte das fOnConnect gefeuert wird. Das Passiert aber nicht. Bzw wenn ich InternalSend manuell anschmeiße liefert er sofort B = 0 . Und ich kann einfach nicht weiter senden.
Wenn ich manuell eine Nachricht schicken will verwände ich "obj.sendbuffer" auf das was ich senden will und starte danach direkt "InternalSend(obj)"

Zum besseren veständnis sollte ich hier mal die klasse THTClient erkären. Im Grunde speichert sie nur nen paar Variablen...

Code: Alles auswählen

THTClient = class
  asocket:TLSocket; // Original Socket
  ping:float; // Ping für nen Ping test
  pingtime:TDateTime; // wann der letzte Ping gesendet wird (zur berechnung benötigt)
  lastconnection:TDateTime; // selbsterklärend
  sendbuffer:string; // HIER wird alles reingeschrieben was gesendet werden soll...
  cansend:boolean; // Variable ob er senden kann...
  owner:TObject;          // want to Connct additional information - define CreateOwner to add you own chosen object...
  public
  constructor Create;
  end;

Code: Alles auswählen

Signatur := nil;

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: kleine Frage zu Strings (lNet)

Beitrag von Displaced »

Hmn, das geht noch einfacher:

Code: Alles auswählen

procedure TServerThread.SocketSend(aSocket: TLSocket);
var
  Sent: Integer;
  Index: Integer;
begin
  Index := GetSocketIndex(aSocket);
  repeat
    Sent := Socket.SendMessage(SendBuffer[Index].Buffer, aSocket);
    if (Sent > 0)  then
       TrimSendBuffer(Index, Sent);
  until (Sent = 0) or (length(SendBuffer[Index].Buffer) = 0);
end;
Der SendBuffer[Index].Buffer ist im endeffekt einfach nen String.. wird halt gesendet solang es geht (Sent <> 0) oder solang was drin ist im Buffer.. Wenn Sent = 0 dann geht's nicht mehr. fertig.
Arbeitet 1A und ist absolut unaufwendig.
Und damit es gesendet wird, wird es vorher in einer Funktion normal gesendet bis es nicht mehr geht und dann in den Buffer geschrieben bis man wieder senden kann.

Code: Alles auswählen

procedure SendDataTo(Index: Integer; Data: String);
var
  sent: Integer;
begin
  if (IsConnected(Index)) then
  begin
 
    if (ssCanSend in TLSocket(PlayerSock[Index]).SocketState) and // wenn man senden kann
        (Length(Server.SendBuffer[Index].Buffer) = 0) then begin // und im buffer nix drin ist !!
      repeat
        sent := Server.Socket.SendMessage(Data,TLSocket(PlayerSock[Index])); // wirds gesendet
        if (sent > 0) then
          data := copy(data, sent+1, length(data));
      until (sent=0) or (length(data) = 0);
    end
    else
      sent := 0;
 
    if (sent = 0) then begin  // amsonsten wirds in den buffer geschrieben und beim nächsten aufruf von OnCanSend gesendet..
      Server.AddSendBuffer(Data, Index);
    end;
  end;
end;

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: kleine Frage zu Strings (lNet)

Beitrag von MAC »

Edit: Original nachricht:

Code: Alles auswählen

Super !
Danke, ich hab einfach mal den zweiten Teil von dir "geklaut", da ich jetzt beim ersten Senden direkt in den Buffer und einfach manuell "onCanSend" aufgerufen hab...
Ich dachte das kommt aufs gleich raus...
Naja, ich kümmer mich heut nachmitag nochmal genau warum meins nicht geklappt hat,warscheinlich ist es die CanSend abfrage, welche ich im onCanSend nicht hatte...
Edit:
Funktioniert doch nicht. dein system kommt bei mir aufs gleich raus wie ich es gang am anfang gemacht habe.
Mit dem

Code: Alles auswählen

sent := Server.Socket.SendMessage(Data,TLSocket(PlayerSock[Index]));
werden auch bei mir alle 200 MB in einem Rutsch versendet. Das bedeutet ich umgehe das onCanSend event indem ich trotzdem alles auf einmal sende --> logische Folge: das versenden einer weiteren Datei nach den 200 MB von Server geht nicht mehr, weil er meint der BUffer sei voll...

Ich verwende also wieder den Aufbau von eben (leicht abgeändert)
Er Sendet einmal 65000 byte, dann in der gleichen repeat schleife noch ein zweites mal. Danach sendet er nix mehr und SendMessage liefert immer nur 0 zurück, OnCanSend wird nicht gefeuert! ist aber zugeordnet
(nehm is als maxmsg = 650; klappt die repeat Schleife 4 mal.)
(nehm is als maxmsg = 650000; klappt die repeat Schleife 1 mal.)

Code: Alles auswählen

procedure THydroThalamusTCP.InternalSend(obj:THTClient);
var
   a,b:integer;
const
  maxmsg = 65000;
begin
if not (ssCanSend in obj.asocket.SocketState) then exit;
repeat
    a := length(obj.sendbuffer);
    if a = 0 then exit;
    case a of
    1..maxmsg:b := aTCP.SendMessage(obj.sendbuffer,obj.asocket);
    else b := aTCP.SendMessage(copy(obj.sendbuffer,1,maxmsg),obj.asocket);
    end;
    if b = a then obj.sendbuffer := ''
             else Delete(obj.sendbuffer,1,b);
until b = 0
end;
 
procedure THydroThalamusTCP.fOnCanSend(aSocket: TLSocket);
var
   obj:THTClient;
begin
obj := FindClientServer(aSocket);
if assigned(obj) then InternalSend(obj)
end;
Gesendet wird dann Folgender Maßen

Code: Alles auswählen

clients[i].sendbuffer += 'Hallo hier kann jetzt die su sendene Nachricht stehen...' + 200 MB;
InternalSend(clients[i]);

Code: Alles auswählen

Signatur := nil;

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: kleine Frage zu Strings (lNet)

Beitrag von Displaced »

Normalerweise sollte er, rein theoretisch, beim senden die Anzahl zurückliefern die er gesendet hat... dann musste das so umschreiben, dass er dann das was er gesendet hab von ursprungstring abzieht und in den buffer schreibt. Ich hab vergessen dass du diese 200MB in einem stuck sendest.. Das mach ich ja nie. Dafür sorgt mein Protokoll dahinter..

Antworten