Type-Length-Value Implementation



  • Ich habe mir überlegt bei meiner client/server anwendung das TLV-Protokoll einzusetzen.
    Bei der Implementation hab ich allerdings noch keine mich überzeugende Idee.

    Wie könnte man dies so einbauen, dass man die Nachrichten leicht parsen kann
    und die struktur um neue Nachrichten einfach erweiterbar ist?!
    Sollte ich enums verwenden oder lieber Konstanten?
    Könnten mir hier evtl. bestimmte Container von nutzen sein?



  • hola

    beispiel:
    nachrichten typ: 1 byte
    laenge: 2 byte
    nachricht x bytes:

    du koenntest fuer jeden nachrichtentyp eine eigene klasse schreiben, welche jedoch alle von einer basisklasse ableiten. dann stopfst du alle z.b. in eine map.
    wenn eine nachricht am socket reinschwirrt, liest du das erste byte, holst dir, falls vorhanden, aus der map das richtige nachrichtenobjekt und uebergibst ihm eventuell den socket, oder liest den stream aus dem socket und uebergibst ihn dem objekt.

    Meep Meep





  • @Meep Meep

    Danke, so in etwa hab ich mir das auch gedacht, aber hab trotzdem noch
    Schwierigkeiten bei der Umsetzung.
    Hat jemand lust ein minimal Beispiel zu geben?

    @Undertaker

    Danke, aber der Code hilft mir leider nicht wirklich.



  • re

    hier ein beispiel. nachdem ich bis jetzt noch nie std::map wirklich gebraucht habe, stimmt der syntax mit ziemlicher sicherheit ueberhaupt nicht. soll aber nur eine grobe veranschaulichung sein, wie das ablaufen koennte.

    class BaseMsg
    {
       public:
          BaseMsg(char t_msg) : msg(t_msg) { }
          virtual ~BaseMsg(void) { }
    
          char getMsgID(void) const { return msg; }
    
       protected:
          char msg;
    };
    
    class TextMsg : public BaseMsg
    {
       public:
          TextMsg(char t_msg) : BaseMsg(t_msg), bytes_read(0) { }
          ~TextMsg(void) { }
          void Execute(SOCKET sock, std::ostream &out)
          {
             short len;
             read_from_socket(sock, &len, sizeof(len));
             char *buffer = new char[len];
             read_from_socket(sock, buffer, len);
             bytes_read += sizeof(len) + len;
             out << buffer;
             delete[] buffer;
          }
    
       private:
          int bytes_read;
    };
    
    void InitializeMessages(std::map<char,BaseMsg*> &map)
    {
       map.insert(std::pair<char,BaseMsg*>(new TextMsg('t'));
       .... /* und weitere falls vorhanden */
    }
    
    void ReadLoop(std::map<char,BaseMsg*> &map)
    {
       ...
       while(socket_is_open)
       {
          char c;
          ReadFromSocket(socket, c);
          BaseMsg *my_msg = map[c];
          my_msg->Execute(socket);
       }
    }
    
    int main(void)
    {
       ...
       std::map<char,BaseMsg*> msg_map;
       InitializeMessages(msg_map);
       ...
       ReadLoop(msg_map); /* wie auch immer */
    }
    

    du koenntest natuerlich auch deine abgeleiteten msg-klassen in dlls, falls du unter windows programmierst, auslagern und sie beim starten dynamisch einbinden.
    hat den vorteil das du dann die software selbst nicht immer wieder neu kompilieren musst, wenn ne neue msg-klasse dazukommt.

    Meep Meep



  • Also Meep Meep, der Code ist ja wohl grauselig wie nur was.
    Du solltest etwas nicht "Message" nennen wenn es ein "Message Processor" oder sowas in der Art ist. Pfui.
    Weiters wirst du in eine Map nicht einfach eine Value einfügen können, es sollte logisch sein dass du immer Key + Value brauchst.

    Und: der Sinn hinter TLV ist ja dass man die Message gleich als ganzes lesen kann, das muss nicht in jeder Funktion die Messages bearbeitet neu stehen.

    Sinnvollerweise liest man erstmal 2 Byte, und dann den Rest. Erst danach übergibt man den ganzen Krempel zur Verarbeitung. Dort erfolgt eben dann das Dispatching (welches du über eine Map realisiert hast, was keine schlechte Idee ist). Das macht den Code wesentlich einfacher.



  • hustbaer schrieb:

    Also Meep Meep, der Code ist ja wohl grauselig wie nur was.
    Du solltest etwas nicht "Message" nennen wenn es ein "Message Processor" oder sowas in der Art ist. Pfui.
    Weiters wirst du in eine Map nicht einfach eine Value einfügen können, es sollte logisch sein dass du immer Key + Value brauchst.

    naja der code war schnell hingekritzelt, da ich mit meinem kater einen termin beim tierdiktor hatte.

    fuer den insert wollte ich eigendlich

    map.insert(std::pair<char,BaseMsg*>('t', new TextMsg('t'));
    

    schreiben. waere das korrekt gewesen ?

    Meep Meep



  • Was habt ihr eigentlich alle gegen make_pair? 😃



  • Meep Meep schrieb:

    fuer den insert wollte ich eigendlich

    map.insert(std::pair<char,BaseMsg*>('t', new TextMsg('t'));
    

    schreiben. waere das korrekt gewesen ?

    jupp



  • Airdamn schrieb:

    @Undertaker
    Danke, aber der Code hilft mir leider nicht wirklich.

    na schade, ist es doch fast eine complette TLV library.
    aber, ich kenne das, manchmal möchte man das rad eben nochmal erfinden. 😉
    --> http://en.wikipedia.org/wiki/Not_Invented_Here
    (nicht böse gemeint)
    🙂



  • Undertaker schrieb:

    Airdamn schrieb:

    @Undertaker
    Danke, aber der Code hilft mir leider nicht wirklich.

    na schade, ist es doch fast eine complette TLV library.
    aber, ich kenne das, manchmal möchte man das rad eben nochmal erfinden. 😉
    --> http://en.wikipedia.org/wiki/Not_Invented_Here
    (nicht böse gemeint)
    🙂

    Das Problem hier ist nicht das ausereinander nehmen von bytes.
    Es ging mir viel mehr darum, dass ich eine Geschickte Verarbeitung der
    eingehenden Nachrichten erreichen möchte.
    z.B. durch eine Message Factory oder durch eine map mit funktionspointern
    für die einzelnen Nachrichten-Typen. So das ich keine if-orgien oder elendig
    lange switches benötige 😉

    Zudem ist es auch finsteres C (string.h, stdio.h) 🤡
    nicht böse gemeint 🙂


Anmelden zum Antworten