Autor Beitrag
oern
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 142

Linux, Win XP
D7
BeitragVerfasst: Mo 03.07.06 13:07 
Ich habe das UDPSockUtil bisher verwendet um records zu versenden.
Aufgrund von diesen Beitrag und das der record zu groß ist möchte ich nicht mehr. Ich möchte nun die Daten Binär über tragen, da mir diese methode als am schnellsten erscheint.
z.Z gehe ich denn umweg über einen stream ich packe zu erste einen Command Integer in den stream und dann alles was für das command gebraucht(single werte etc.). Das wird dann alles in einen array of byte gepacket und mit SendBuf geschickt (so soll es zumindest werden)

Leider kommt da nichts vernüftiges an :(

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
// Inhalt des Streams wird ein den array of byte gepackt und gesendet.
procedure TGLSN_Engine.UDPBroadcastStream(S: TMemoryStream);
var
  b: array of byte;
begin
  setlength(b, s.Size);
  s.Read(b, s.Size);
  UdpSockUtil.SendBuf(b, sizeof(b));
end;

// UDP empfangen
procedure TGLSN_Engine.UdpSockUtilReceive(Sender: TObject);
var
  Len: Integer;
  vonIP: in_addr;
  b: array of byte;
  s: TMemoryStream;
  C: Integer;
begin
  Len := UdpSockUtil.ReceiveLength;
  if (Len >= 4then begin
    //if inet_ntoa(vonIP)<>IP.LocalIP then
    begin
      setlength(b, len);
      UdpSockUtil.ReceiveBuf(b, sizeof(b), vonIP);
      try
        s:= TMemoryStream.Create;
        s.Write(b, sizeof(b));
        s.Read(c, sizeof(c));
        // c ist hier 0 muss aber 1 sein
      finally
        s.Destroy;
      end;
    end;
  end;
end;

// etwas senden
procedure TGLSN_Engine.StartServer;
var
  s: TMemoryStream;
  i: integer;
begin
  // Server anschalten
  Server.Open;
  // Alle sagen wir haben einen Server
  try
    s:= TMemoryStream.Create;
    i:= 1;
    s.Write(i, SizeOf(Integer) );
    UDPBroadcastStream(s);
  finally
    s.Destroy;
  end;
end;


Was mache ich da nur falsch ?

mfg, Björn

_________________
Ich hab eine Signatur
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 03.07.06 14:33 
Moin!

Sorry, bitte nicht persönlich nehmen, aber du wagst dich da mit deinem Wissensstand an ein Problem heran, das dir (z.Zt. wohl noch) ein bis zwei Nummern zu gross ist... :?

Ich will dir zunächst mal nur den Fehler zeigen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TGLSN_Engine.UDPBroadcastStream(S: TMemoryStream);  
var  
  b: array of byte;  
  BytesSent: Integer;
begin  
  setlength(b, s.Size);  
  s.Read(b, s.Size);  
  BytesSent := UdpSockUtil.SendBuf(b, sizeof(b));
  ShowMessage(IntToStr(BytesSent));
end;

Wenn du das ausführst, wirst du vermutlich eine "0" angezeigt bekommen, was bedeuten soll, dass keine Bytes übertragen wurden - zumindest aber wird dir dieser Code zeigen, dass du nicht die Menge Bytes gesendet hast, die du senden wolltest. Das könnte - wie du schon gesagt hast - daran liegen, dass du mehr als 512 Bytes (bzw. besser die Konstante USU_MAX_DGRM_SIZE nehmen) senden willst, was UDP nicht kann. Du mußt also in diesem Fall selbst für die Segmentierung sorgen. Vor allem aber mußt du auch schauen, ob du alles was du senden wolltest, auch gesendet hast (den Rückgabewert von SendBuf auswerten).

Nächstes Problem (in folgedesssen): Wenn du mit UDP-Paketen arbeitest, dann muss dir klar sein, dass die Sendereihenfolge nicht zwingend die Empfangsreihenfolge abbildet - es könnten sogar schlimmstenfalls Pakete fehlen! :shock: Du mußt also eine eigene Sicherungsschicht implementieren (dein Stream muss ja konsistent bleiben, sonst ist er wertlos), und das wird nicht ganz einfach sein.

Deshalb mein Tipp für dich: Wickle nur die Signalisierung über UDP-Broadcasts ab und mach den Datentransfer mit TCP-Verbindungen direkt an die entsprechenden IPs, sonst kommst du in Teufelsküche! :| ;)

Der Ansatz mit dem Stream als record-Ersatz ist ja schon ganz nett, aber wesentlich einfacher wäre ein Protokoll, das ich dir hiermit nochmal ans Herz legen möchte. ;) Das ganze gibt´s übrigens auch in der UDP-Variante.

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
oern Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 142

Linux, Win XP
D7
BeitragVerfasst: Mo 03.07.06 15:06 
Zitat:
Sorry, bitte nicht persönlich nehmen, aber du wagst dich da mit deinem Wissensstand an ein Problem heran, das dir (z.Zt. wohl noch) ein bis zwei Nummern zu gross ist... :?

Das kann sein bzw. ist bestimmt so. Aber ich habe mich auch durch die 3D darstellung gekämpft.

Zitat:
Wenn du das ausführst, wirst du vermutlich eine "0" angezeigt bekommen, was bedeuten soll, dass keine Bytes übertragen wurden - zumindest aber wird dir dieser Code zeigen, dass du nicht die Menge Bytes gesendet hast, die du senden wolltest. Das könnte - wie du schon gesagt hast - daran liegen, dass du mehr als 512 Bytes (bzw. besser die Konstante USU_MAX_DGRM_SIZE nehmen) senden willst, was UDP nicht kann. Du mußt also in diesem Fall selbst für die Segmentierung sorgen. Vor allem aber mußt du auch schauen, ob du alles was du senden wolltest, auch gesendet hast (den Rückgabewert von SendBuf auswerten).

Nach BytesSent werden 4 byte gesendet also die größe eines integers.


Zitat:
Nächstes Problem (in folgedesssen): Wenn du mit UDP-Paketen arbeitest, dann muss dir klar sein, dass die Sendereihenfolge nicht zwingend die Empfangsreihenfolge abbildet - es könnten sogar schlimmstenfalls Pakete fehlen! :shock: Du mußt also eine eigene Sicherungsschicht implementieren (dein Stream muss ja konsistent bleiben, sonst ist er wertlos), und das wird nicht ganz einfach sein.
Wenn ich pro Sekunde 30 Pakete sende dann können da ruhig 1 oder 2 wegkommen müssen halt nur so 24 ankommen damit alles flüssig läuft.

Zitat:
Der Ansatz mit dem Stream als record-Ersatz ist ja schon ganz nett, aber wesentlich einfacher wäre ein Protokoll, das ich dir hiermit nochmal ans Herz legen möchte. ;) Das ganze gibt´s übrigens auch in der UDP-Variante.

Ich arbeiten an eine shooter da müssen viele daten über das netzwerk gesendet werden und die CPU wird immer sehr stark belastet (immer 100%), ich denke dann strings parsen wäre zu viel rechen aufwand. Dann wär noch der Traffic durch das Protokoll höher als durch binäre daten da vieles im klartext übertragen wird oder liege ich da falsch ?

mfg, Björn

_________________
Ich hab eine Signatur
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 03.07.06 15:36 
Moin!

user profile iconoern hat folgendes geschrieben:
Wenn ich pro Sekunde 30 Pakete sende dann können da ruhig 1 oder 2 wegkommen müssen halt nur so 24 ankommen damit alles flüssig läuft.

Ja, das ist korrekt gedacht; allerdigs: du willst einen Stream übertragen, der größer als die maximale UDP-Paketgröße ist -> mehrere Pakete -> davon darf dann aber keins fehlen, sonst ist der Stream ja nicht vollständig -> Sicherungsschicht!

Alternative: nie mehr als max. Paketgröße auf einmal (zusammenhängend) senden. ;)

user profile iconoern hat folgendes geschrieben:
Ich arbeiten an eine shooter da müssen viele daten über das netzwerk gesendet werden und die CPU wird immer sehr stark belastet (immer 100%), ich denke dann strings parsen wäre zu viel rechen aufwand. Dann wär noch der Traffic durch das Protokoll höher als durch binäre daten da vieles im klartext übertragen wird oder liege ich da falsch?

Naja, vom Grundsatz her hast du schon recht, allerdings kommt es immernoch darauf an, wie "komplex" der Parser denn gestrickt ist, und ob das Strings sein müssen, es lassen sich ja auch Binärdaten transportieren. Aber ganz allgemein: ich glaube nicht, dass der Protokollparser den Kohl fett macht, bei einer solchen Grafikanwendung, da sind andere Dinge, die die Last erzeugen. ;)

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
oern Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 142

Linux, Win XP
D7
BeitragVerfasst: Mo 03.07.06 18:40 
Ich hab alle Fehler gefunden und es funktioniert :D
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
// Inhalt des Streams wird ein den array of byte gepackt und gesendet.
procedure TGLSN_Engine.UDPBroadcastStream(S: TMemoryStream);
var
  b: array of byte;
  BytesSent: Integer;
begin
  setlength(b, s.Size);
  s.Position:=0;
  s.Read(b[0], s.Size);
  BytesSent:=UdpSockUtil.SendBuf(b[0], sizeof(b));
  if Loging then begin
    if BytesSent=0 then begin
      LogMsg('UDP Error: 0 Byte gesendet.');
    end;
    if BytesSent>512 then begin
      LogMsg('UDP Warnung: Paketgröße von 512 Bytes überschriten.');
    end;
  end;
end;

// UDP empfangen
procedure TGLSN_Engine.UdpSockUtilReceive(Sender: TObject);
var
  Len: Integer;
  vonIP: in_addr;
  b: array of byte;
  s: TMemoryStream;
  C: Integer;
begin
  Len := UdpSockUtil.ReceiveLength;
  if (Len >= 4then begin
    //if inet_ntoa(vonIP)<>IP.LocalIP then
    begin
      setlength(b, len);
      UdpSockUtil.ReceiveBuf(b[0], sizeof(b), vonIP);
      try
        s:= TMemoryStream.Create;
        s.Write(b[0], sizeof(b));
        s.Position:=0;
        s.Read(c, sizeof(c));
        case C of
          GLSN_StartServer{1}:begin
            IPList.addip(inet_ntoa(vonIP));
          end;
        else
          // C is kein GLSN Commmand
        end;
      finally
        s.Destroy;
      end;
    end;
  end;
end;


Zitat:
Ja, das ist korrekt gedacht; allerdigs: du willst einen Stream übertragen, der größer als die maximale UDP-Paketgröße ist -> mehrere Pakete -> davon darf dann aber keins fehlen, sonst ist der Stream ja nicht vollständig -> Sicherungsschicht!

Solange ich über die UDP Verbindung nur Pakete kleiner 512 Byte sende hab ich damit keine problem, und die positionen belegen nur so um die 20 Byte.

Zitat:

Naja, vom Grundsatz her hast du schon recht, allerdings kommt es immernoch darauf an, wie "komplex" der Parser denn gestrickt ist, und ob das Strings sein müssen, es lassen sich ja auch Binärdaten transportieren. Aber ganz allgemein: ich glaube nicht, dass der Protokollparser den Kohl fett macht, bei einer solchen Grafikanwendung, da sind andere Dinge, die die Last erzeugen. ;)

Aber dennnoch schlägt ein protokol mit ein paar byte zu buche, die man vermeiden kann.

mfg, Björn

_________________
Ich hab eine Signatur
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 03.07.06 22:55 
Moin!

user profile iconoern hat folgendes geschrieben:
Ich hab alle Fehler gefunden und es funktioniert :D

Schön; ich habe ja auch nicht behauptet, dass es nicht geht. ;)

user profile iconoern hat folgendes geschrieben:
Solange ich über die UDP Verbindung nur Pakete kleiner 512 Byte sende hab ich damit keine problem, und die positionen belegen nur so um die 20 Byte.

Genau das habe ich geschrieben... ;)

user profile iconoern hat folgendes geschrieben:
Aber dennnoch schlägt ein protokol mit ein paar byte zu buche, die man vermeiden kann.

Sorry, aber IMHO sind weder die paar Bytes Protokolloverhead noch die paar Nanosekunden das, was dir die "Zeit klaut". Da halte ich schon eher die Windows-Nachrichtenverarbeitung, die du implizit über meine Komponente verwendest, für einen Performance-Fresser. Wenn du wirklich auf dieser Ebene optimieren willst, dann mußt du blocking-socket-calls machen und mit Threads arbeiten. Mit den WSA-Messages vergräbst du SEHR VIEL mehr Zeit, als dich jeder Protokoll-Parser jemals kosten könnte. :? ;)

cu
Narses

PS: Wenn dein Problem gelöst ist, markierst du den Thread dann noch entsprechend? Danke. ;)

_________________
There are 10 types of people - those who understand binary and those who don´t.
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 04.07.06 11:25 
Moin!

Was mir noch (später) aufgefallen ist: du kannst das Versenden vermutlich leichter mit einem TStringStream abwickeln, so z.B.:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure TForm1.Button1Click(Sender: TObject);
  var
    StrStream: TStringStream;
    IntVar: Integer;
    DoubleVar: Double;
begin
  StrStream := TStringStream.Create(''); // Objekt erzeugen
  IntVar := $4711// Integer vorbelegen
  StrStream.Write(IntVar,SizeOf(IntVar)); // schreiben
  DoubleVar := 1.41421// Double vorbelegen
  StrStream.Write(DoubleVar,SizeOf(DoubleVar)); // schreiben
  StrStream.WriteString('Test'); // String anfügen
  // Daten senden und prüfen, ob alles gesendet wurde
  if (UdpSockUtil1.SendText(StrStream.DataString) <> StrStream.Size) then
    ShowMessage('Fehler beim Senden!');
  StrStream.Free; // Stream wieder freigeben
end;

Damit sparst du dir den zusätzlichen Speicher und das Kopieren (du bist doch so ein Optimierungsfuchs... ;)).

Beim Empfangen solltest du auch dann Lesen, wenn weniger als 4 Bytes eintreffen. Ist dann halt eine Fehlersituation, aber irgendwohin sollten die Daten schon "verschwinden":
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
procedure TForm1.UdpSockUtil1Receive(Sender: TObject);
  var
    StrStream: TStringStream;
    vonIP: in_addr;
    IntVar: Integer;
begin
  // Daten vom Socket lesen, per StringStream in einen Stream verwandeln
  StrStream := TStringStream.Create(UdpSockUtil1.ReceiveText(vonIP));
  StrStream.Position := 0// von Vorne lesen
  if (StrStream.Size >= 4then begin // genug Daten da?
    StrStream.Write(IntVar,SizeOf(IntVar)); // Integer lesen
    case IntVar of // Fallunterscheidung...
      $4711:
        ShowMessage('Echt komisches Wasser!');
      else
        ShowMessage('Keine Ahnung!?');
    end;
  end
  else
    ShowMessage('Zu wenig Daten gelesen!');
  StrStream.Free; // Objekt freigeben
end;


cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
oern Threadstarter
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 142

Linux, Win XP
D7
BeitragVerfasst: Di 04.07.06 11:33 
Danke den Type kannt ich gar nicht :)

_________________
Ich hab eine Signatur
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 04.07.06 12:06 
Moin!

Wenn dein Problem gelöst ist, markierst du den Thread dann noch entsprechend? Danke. ;)

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.