Entwickler-Ecke

Internet / Netzwerk - Protokoll selbst implementieren


matze - Mi 27.02.08 11:13
Titel: Protokoll selbst implementieren
Hallo.

Ich stehe im Moment vor der Aufgabe, dass ich das UDP Protokoll des Mac-Programmes Growl implementieren muss.
Leider habe ich sowas noch nie gemacht und bin im Moment auch ein bisschen hilflos.

Das sind meine bisherigen Bemühungen. Der Code ist grauselig, aber ich wollte einfach mal nur einen Test programmieren.

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:
var
  Form1: TForm1;
  Growl_App_Name: String = 'Matze';
  Growl_Notification_Name: String = 'Hinweis';
const
  Growl_Protocol_version: byte = 1;
  Growl_Type_Registration: byte = 5;
  Growl_Type_Notification: byte = 1;


procedure Send;
var
  buf: TStringStream;
  len: integer;
  sbuf:string;
begin
  Growl_App_Name := AnsiToUtf8(Growl_App_Name);
  Growl_Notification_Name := AnsiToUtf8(Growl_Notification_Name);
  buf := TStringStream.Create('');
  try
    buf.WriteBuffer(Growl_Protocol_version,SizeOf(Growl_Protocol_version));
    buf.WriteBuffer(Growl_Type_Registration,SizeOf(Growl_Type_Registration));
    len := length(Growl_App_Name);
    buf.WriteBuffer(len,2);
    len := 1;
    buf.WriteBuffer(len,1);
    buf.WriteBuffer(len,1);
    buf.WriteBuffer(Growl_App_Name,length(Growl_App_Name));
    buf.WriteBuffer(len,2);
    buf.WriteBuffer(Growl_Notification_Name,length(Growl_Notification_Name));
    buf.WriteBuffer(len,2);

    sbuf := buf.DataString;

    IdUDPClient1.Send('192.168.0.37',9887,sbuf);

  finally
    buf.Free;
  end;
end;


Die Protokoll-Spezifiationen sind hier [http://growl.info/documentation/developer/protocol.php]. Das soll angeblich auch recht einfach gehen, aber anscheinend bin ich zu blöd dazu.
Es gibt auch einige Implementierungen in PHP (z.b. hier [http://php-growl.googlecode.com/svn/trunk/class.growl.php]), aber dazu kann ich zu wenig PHP um das nach Delphi portieren zu können.

Kann mir jemand bei meinem Problem helfen!

Danke schon mal im voraus !


opfer.der.genauigkeit - Mi 27.02.08 11:52

Wo scheitert es denn genau?
Evtl. entspricht der Ausgabebuffer nicht dem Protokoll? *tipp ins blaue*


matze - Mi 27.02.08 11:58

ja vermutlich. Also das Programm, das ich ansprechen möchte (Growl) zeigt auf jeden Fall keine Reaktion. Ich habe zum Test mal die PHP Implementierungen ausprobiert. Da geht es.
Also vermutlich setzt ich die Protokollspezifikationen nicht richtig um.

Die Frage ist jetzt: Wie macht man das richtig ?


opfer.der.genauigkeit - Mi 27.02.08 12:28

Ich kann es leider nicht testen, den PHP-Code kann ich dir aber gerne übersetzen, wenn dir das weiterhelfen würde.
Bezweifel ich aber irgendwie. :)

Meine Vorgehensweise wäre folgende.

Ich würde ein packed record definieren und die Werte schreiben, die ich an den jeweiligen Protokolldefinitionen haben moechte.
Dann würde ich das Ergebnis in einen Bytearray kopieren (imho ist das aber nicht mal noetig)...

Da ich leider auch nicht testen kann, kann ich nur theoretische Ansätze bieten.
Was mir im PHP-Code aufgefallen ist, für pack [http://at.php.net/pack] werden noch zusaetzliche Formatierungsangaben gemacht. Evtl. gibt dir das weiteren Aufschluß darüber, was evtl. nicht stimmt.

Und vergleiche dochmal den Buffer vom PHP-Code mit deinem. :gruebel:

//Edit: Wenn du mit anderen Sprachen besser bewandert bist (z.B. C++), dann schau doch mal bei koders.com, ob du da den Code findest.


Sinspin - Mi 27.02.08 13:01

Was mir auffällt ist, das du TStringStream verwendest.
Eigentlich stört es nicht sonderlich was für einen Stream man nimmt aber könnte es sein das bei dieser Variante irgendwas vor oder hinter den Daten steht das da nicht hinsoll?
Delphi hat ja die Angewohnheit vor Strings immer die Länge schreiben zu müssen. Oder einen Zeilenumbruch am Ende anzufügen.


matze - Mi 27.02.08 13:07

Den StringStream habe ich genommen, weil die Indy UDP Komponente nur Strings annimmt...

@Opfer.der.genaugkeit: Wie soll ich denn meinen Output mit dem von dem PHP Script vergleichen?


opfer.der.genauigkeit - Mi 27.02.08 13:22

Z.B.: echo oder var_dump und anzeigen lassen.


Xentar - Mi 27.02.08 13:33

Man könnte auch mit diversen Tools, z.B. WireShark, den Netzwerktraffic mitschneiden, und dann die Pakete, die das PHP Script sendet, mit deinen vergleichen.

Edit: Ich seh grad, du sendest keine MD5 Checksumme mit? Vllt. liegts schon daran?


matze - Mi 27.02.08 13:34

user profile iconopfer.der.genauigkeit hat folgendes geschrieben:
Z.B.: echo oder var_dump und anzeigen lassen.

Das bringt mir nichts, da seh ich nur Kauderwelsch (ist ja auch ein binär Protokoll)

Die Checksumme kann ich ja laut dem Protokoll auch weg lassen, wenn ich den Typ 5 (NoAuth) nehme.


opfer.der.genauigkeit - Mi 27.02.08 13:46

Probier es mal mit sprintf [http://de.php.net/manual/en/function.sprintf.php].
Damit kannst du das Ausgabeformat bestimmen, Hex z.B.


Narses - Mi 27.02.08 14:12

Moin!

Mir ist aufgefallen: Ich kann in der Doku nix über den byte-sex finden. :gruebel:

Die MAC-Jungs stehen ja auf msb (aus Motorola-Zeiten, von mir aus auch network-byte-order, was einem besser gefällt), aber du schreibst in deinem Code lsb (eben Intel-typisch). Da schonmal nach geschaut? :nixweiss:

cu
Narses


matze - Mi 27.02.08 14:23

na gut, aber so an sich: wäre das Protokoll denn technisch richtig implementiert?
Also mache ich das richtig mit dem WriteBuffer Zeug und dem Versenden mit dem String?


Narses - Mi 27.02.08 14:33

Moin!

user profile iconmatze hat folgendes geschrieben:
na gut, aber so an sich: wäre das Protokoll denn technisch richtig implementiert?
Sieht so aus (hab´s aber auch nicht im Detail nachgezählt und abgehakt...) :nixweiss:

user profile iconmatze hat folgendes geschrieben:
Also mache ich das richtig mit dem WriteBuffer Zeug und dem Versenden mit dem String?
Schick´s dir doch einfach mal selbst und mach einen HEX-Dump, den vergleichst du dann mit der Doku. Wenn die Anzahlen/Positionen stimmen, dann kannste nochmal die ByteOrder tauschen. :idea:

cu
Narses


matze - Mi 27.02.08 14:40

OK ich habe mir jetzt mal WireShark geladen und damit die beiden Pakete verglichen:

Das von PHP, das funktioniert:

Quelltext
1:
2:
3:
00000000  01 00 00 05 01 01 4d 61  74 7a 65 00 07 57 61 72 ......Ma tze..War
00000010  6e 69 6e 67 00 89 c8 e4  35 33 a8 e8 08 19 f0 d5 ning.... 53......
00000020  62 d9 cd 7f 88                                   b....


Meins, das nicht funktioniert:

Quelltext
1:
2:
00000000  01 05 05 00 01 01 28 c3  aa 00 c0 01 00 c0 20 ac ......(. ...... .
00000010  00 01 8d 40 01 00                                ...@..


Sagt das den Experten was?
Also immerhin sehe ich, dass der Name der Application (Matze) nicht mitgesendet wird, bei meinem Output...


Narses - Mi 27.02.08 15:00

Moin!

user profile iconmatze hat folgendes geschrieben:
Sagt das den Experten was?
Jup. ;)

user profile iconmatze hat folgendes geschrieben:
Das von PHP, das funktioniert:

Quelltext
1:
2:
3:
00000000  01 00 00 05 01 01 4d 61  74 7a 65 00 07 57 61 72 ......Ma tze..War
00000010  6e 69 6e 67 00 89 c8 e4  35 33 a8 e8 08 19 f0 d5 ning.... 53......
00000020  62 d9 cd 7f 88                                   b....
Also, wie ich schon vermutete: msb! :idea: Das schreibst du also schonmal "falsch rum".

user profile iconmatze hat folgendes geschrieben:
Also immerhin sehe ich, dass der Name der Application (Matze) nicht mitgesendet wird, bei meinem Output...
Stimmt... :? Passiert mir auch immer wieder, du schreibst den String-Pointer, nicht den Inhalt: ;)

Delphi-Quelltext
1:
buf.WriteBuffer(PChar(Growl_App_Name)^,length(Growl_App_Name));                    

cu
Narses


matze - Mi 27.02.08 15:12

Ah. verdammt. Da hätte ich aber auch selber drauf kommen können !
Und wie macht man das mit dem MSB?


Narses - Mi 27.02.08 15:28

Moin!

user profile iconmatze hat folgendes geschrieben:
Ah. verdammt. Da hätte ich aber auch selber drauf kommen können !
:D

user profile iconmatze hat folgendes geschrieben:
Und wie macht man das mit dem MSB?

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
uses
  ..., WinSock;

function htonl;             external    winsocket name 'htonl';
function htons;             external    winsocket name 'htons';
function ntohl;             external    winsocket name 'ntohl';
function ntohs;             external    winsocket name 'ntohs';

cu
Narses


matze - Mi 27.02.08 15:31

Äääähhh... Ich steh aufm Schlauch.

Wie wendet man das an?


Narses - Mi 27.02.08 15:35

Moin!

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var
  HostBytes, NetBytes: Word;
begin
  HostBytes := 7;
  NetBytes := htons(HostBytes);
  ShowMessage(IntToHex(HostBytes,4)+#13+IntToHex(NetBytes,4));
Vorsicht Falle, die Anzeige ist genau andersrum, als die Bytefolge im Speicher! :mahn:

cu
Narses


matze - Mi 27.02.08 15:59


Quelltext
1:
2:
00000000  01 05 00 05 01 01 4d 61  74 7a 65 00 07 57 61 72 ......Ma tze..War
00000010  6e 69 6e 67                                      ning

Mein neuer Code

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:
var
  buf: TStringStream;
  len: byte;
  app_length: Word;
  sbuf:string;
begin
  Growl_App_Name := AnsiToUtf8(Growl_App_Name);
  Growl_Notification_Name := AnsiToUtf8(Growl_Notification_Name);
  buf := TStringStream.Create('');
  try
    //Version schreiben
    buf.WriteBuffer(Growl_Protocol_version,SizeOf(Growl_Protocol_version));
    //Typ schreiben
    buf.WriteBuffer(Growl_Type_Registration,SizeOf(Growl_Type_Registration));
    //Länge des Applikationsnamen schreiben
    app_length := htons(length(Growl_App_Name));
    buf.WriteBuffer(app_length,SizeOf(app_length));
    //Anzahl der Warnungen und der Defaultwarnungen schreiben
    len := 1;
    buf.WriteBuffer(len,1);
    buf.WriteBuffer(len,1);
    // Namen der Applikation schreiben
    buf.WriteBuffer(PChar(Growl_App_Name)^,length(Growl_App_Name));
    // Länge der Warnung schreiben
    app_length := htons(length(Growl_Notification_Name));
    buf.WriteBuffer(app_length,SizeOf(app_length));
    // Namen der Warnung schreiben
    buf.WriteBuffer(PChar(Growl_Notification_Name)^,length(Growl_Notification_Name));
    buf.WriteBuffer(pos,sizeOf(pos));

    sbuf := buf.DataString;
    IdUDPClient1.Send('192.168.0.37',9887,sbuf);
  finally
    buf.Free;
  end;

Also soweit schaut das schon gut aus. Danke mal an alle bis hierher !!

Ich bin jetzt in der Spezifikation so weit, dass ich eine "Notification" schreiben kann. So.
Laut Spezifikation kann man dann noch mehrere Warnungen wiederholen, was ich aber nicht mache. Am Ende soll man aber noch einen Index auf die Warnung schreiben. (Der DEFAULTS Block). Da muss ich ehrlich sagen, dass ich nicht verstehe, wie ich das machen soll...


matze - Do 28.02.08 10:06

Hallo.

OK. Nach einer langen Nacht habe ich alles hinbekommen und kann jetzt mal die Basis-Kommandos an den Growl-Client senden!
Ich möchte mich ganz herzlich bei allen Bedanken, die mich gestern ertragen haben und mir geholfen haben. Wenn das Projekt dann fertig ist, werde ich eine Klasse draus bauen und das dann hier im Forum veröffentlichen.

Nochmal danke !

Matze


matze - So 16.11.08 13:56

Die Klasse ist schon lange fertig.
Ich hatte nur vergessen, das hier auch zu posten.

Also: http://www.delphi-forum.de/topic_81271.html und http://growl.matf.de