Protokoll schreiben



  • Hallo zusammen,

    ich habe folgendes Problem. Es soll über Client/Server Daten untereinander ausgetauscht werden. Client und Server wurden schon implementiert sowie auch das Versenden von char* ist mit TCP möglich. Ich möchte aber ein eigenes Protokoll dafür entwickeln weiss aber leider nicht wie ich anfangen soll.

    Habe mir so etwas vorgestellt: dataID, datum(zeit), payload

    Ich bräuchte ein Ruck für den Start. Danke jetzt schon im Voraus!

    Grüße



  • Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C++ (auch C++0x) in das Forum Rund um die Programmierung verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Was soll das Protokoll denn können, bzw. was soll das Programm nachher können?



  • cooky451 schrieb:

    Was soll das Protokoll denn können, bzw. was soll das Programm nachher können?

    Hi nochmals,
    das Protokoll sollte gewisse Informationen von/zu Server bzw Client senden können. Ich möchte versuchen via dieser Informationen, zwei unterschiedliche Programme parallel laufen zu lassen, die zwischendurch zeitlich synchronisieren und dabei Daten via Protokoll austauschen.

    Hoffe es ist einigermassen verständlich erklärt 🙂

    Danke im Voraus!



  • Du fängst an, indem du den Werten in deinem Protokoll eine Reihenfolge und eine Repräsentation gibst. Die Byte-Reihenfolge musst du auch festlegen.
    Das könnte der Aufbau sein:

    <32 bit> "dataID"
    <32 bit> Zeit
    <32 bit> payload Länge
    <N * 8 bit> payload
    


  • TyRoXx schrieb:

    Du fängst an, indem du den Werten in deinem Protokoll eine Reihenfolge und eine Repräsentation gibst. Die Byte-Reihenfolge musst du auch festlegen.
    Das könnte der Aufbau sein:

    <32 bit> "dataID"
    <32 bit> Zeit
    <32 bit> payload Länge
    <N * 8 bit> payload
    

    Ist es nun möglich via TCP eine solche Datenstruktur zu versenden? Außerdem wie sieht was du oben geschrieben hast unter c++ aus?
    Noch etwas: ich möchte als Payload-Typ mal ein double versenden und mal char* die variable Längen aufweisen wie kann ich dies realisieren?

    Eine Menge Fragen sorry dafür, aber ich steh voll auf Schlauch und habe auch keine Vorstellung wie es weitergehen soll 🙂



  • Erweitere das Ganze doch um ein Flag, das den Typ des Payloads anzeigt.

    <32 bit> "dataID"
    <32 bit> Zeit
    <32 bit> payload Länge
    <8 bit> Flag
    <N * 8 bit> payload
    

    Dann legst du z.b. fest, dass bei FLAG == 0x01 ein Double gesendet wird und bei 0x02 ein float etc.



  • protokollant schrieb:

    Erweitere das Ganze doch um ein Flag, das den Typ des Payloads anzeigt.

    <32 bit> "dataID"
    <32 bit> Zeit
    <32 bit> payload Länge
    <8 bit> Flag
    <N * 8 bit> payload
    

    Dann legst du z.b. fest, dass bei FLAG == 0x01 ein Double gesendet wird und bei 0x02 ein float etc.

    Ich bin jetzt davon ausgegangen, dass "dataID" den Typ des Paketes angeben soll. Im Payload kann abhängig von dem Typ natürlich alles Mögliche stehen.

    Ein Beispiel, wie man so ein Protokoll schreibend implementieren kann:

    #include <string>
    #include <cstring>
    #include <cassert>
    #include <cstdint>
    
    typedef std::string Buffer;
    
    struct BinaryWriter
    {
      Buffer &buffer;
    
      explicit BinaryWriter(Buffer &buffer)
        : buffer(buffer)
      {
      }
    
      void write(const char *data, size_t size)
      {
        buffer.append(data, data + size);
      }
    
      void overwrite(size_t position, const char *data, size_t size)
      {
        assert(position + size <= buffer.size());
        std::memcpy(&buffer[position], data, size);
      }
    
      template <class POD>
      void write(POD value)
      {
        write(reinterpret_cast<const char *>(&value), sizeof(value));
      }
    
      template <class POD>
      void overwrite(size_t position, POD value)
      {
        overwrite(position, reinterpret_cast<const char *>(&value), sizeof(value));
      }
    
      void writeString32(const std::string &str)
      {
        write(static_cast<std::uint32_t>(str.size()));
        write(str.data(), str.size());
      }
    };
    
    struct PacketWriter : BinaryWriter
    {
      size_t sizePosition;
      size_t payloadPosition;
    
      PacketWriter(Buffer &buffer, std::uint32_t id, std::uint32_t time)
        : BinaryWriter(buffer)
      {
        write(id);
        write(time);
    
        //die Position der Länge für spätere Korrektur speichern
        sizePosition = buffer.size();
        write(std::uint32_t(0));
    
        //um die Länge des Inhaltes auszurechnen
        payloadPosition = buffer.size();
      }
    
      void finish()
      {
        size_t currentPos = buffer.size();
        std::uint32_t length = static_cast<std::uint32_t>(currentPos - payloadPosition);
    
        overwrite(sizePosition, length);
      }
    };
    
    int main()
    {
      Buffer packet;
      PacketWriter writer(packet, 123, 456);
    
      std::string message = "hallo";
      writer.writeString32(message);
    
      writer.finish();
    
      //packet kann nun versendet werden..
    }
    


  • Mit TCP ist es möglich alles zu versenden was du willst 🙂

    Du kannst die Daten auch einfach als ASCII-Text senden, nach dem Muster "Parameter1=Wert1\n" usw.
    Das ist recht einfach zu schreiben, parsen und analysieren und recht flexibel was variable Länge angeht.



  • @TyRoXx Danke dir für den Code hast dir die Mühe gemacht. Ich werde versuchen dies in meinen Program bzw Projekt hinzuzunehmen und falls Fragen auftauchen müsste ich euch nochmals stören. Könntest du kurz beschreiben was da in groben passiert? Danke dir!

    @scrontch es mir klar, dass ich mit TCP alles versenden. Wir gesagt ich steh voll auf dem Schlauch kann auch daran liegen, dass ich keine Pause hatte 🙂

    @protokollant danke dir auch für diesen Hinweis ist mir nicht eingefallen ist aber eine Simple Lösung.



  • J.Wayne schrieb:

    @TyRoXx Danke dir für den Code hast dir die Mühe gemacht. Ich werde versuchen dies in meinen Program bzw Projekt hinzuzunehmen und falls Fragen auftauchen müsste ich euch nochmals stören. Könntest du kurz beschreiben was da in groben passiert? Danke dir!

    Kann ich schon, mach ich aber nicht.
    Hast du eine konkrete Frage?



  • template <class POD>
      void write(POD value)
      {
        write(reinterpret_cast<const char *>(&value), sizeof(value));
      }
    
      template <class POD>
      void overwrite(size_t position, POD value)
      {
        overwrite(position, reinterpret_cast<const char *>(&value), sizeof(value));
      }
    
      void writeString32(const std::string &str)
      {
        write(static_cast<std::uint32_t>(str.size()));
        write(str.data(), str.size());
      }
    

    Diesen Part habe ich nicht so ganz verstanden. Warum hier ein Template nötig ist und die Methoden(write u overwrite) doppelt vorkommen? Ich möchte dir nicht auf die Füsse tretten verstehe mich nicht falsch 🙂



  • write hängt einen Wert an und overwrite überschreibt den Wert an einer früheren Position.
    Das sind Templates, damit das mit allen primitiven Datentypen funktioniert.
    Die Methoden haben dann noch allgemeinere nicht-Template-Versionen, die beliebige Daten annehmen.
    Falls dir das syntaktisch nicht klar ist, beschäftige dich nochmal mit C++ an sich.



  • Hallo nochmals,

    das von dir (TyRoXx) implementierte Code hat folgenden Konstrukt:
    `<32 bit> "dataID"

    <32 bit> Zeit

    <32 bit> payload Länge

    <N * 8 bit> payload `

    Liege ich da richtig oder nicht? Was soviel heißt wie, mein Protokoll weist eine variable Länge auf.

    Danke im Voraus!



  • Ja, der Header ist wie angegeben, die Paketlänge ist variabel (wie denn sonst).



  • Hallo TyRoXx ,

    ich habe deinen Code eingetippt und wiederum verusucht die Standardwerte zu bekommen und bin daber auf ein paar kleine Fehler gestoßen. Nach deinem Code sind jeweils 32-Bit für die ID vergeben, das wiederum ein uint32 ist. Mit

    std::string id;
    id.append(packet,0,4);
    

    kann ich den String herauslesen. wie bekomme ich aus dieser wieder ein uint32 ?



  • TyRoXx ich hätte da noch eine Frage bzw. eine Bemerkung. Das Auslesen der Zahlenwerte für die ID funktioniert wunderbar in Bereich von 0-127 sobald die Zahl größer ist kommt Unfug heraus. Wie könnte dies behoben werden?



  • Beobachter01 schrieb:

    std::string id;
    id.append(packet,0,4);
    

    kann ich den String herauslesen. wie bekomme ich aus dieser wieder ein uint32 ?

    Interessante Idee, aber den string id kann man sich sparen.
    Du holst dir mit &packet[0] einen Zeiger auf den Speicher und kopierst mit memcpy in die Variablen hinein. Um die nächste Variable zu erhalten, einfach den Zeiger erhöhen:

    std::string incoming;
    const char *position = &incoming[0];
    const char * const end = position + incoming.size();
    
    std::uint32_t dataId;
    if (position + sizeof(dataId) > end)
    {
    	//unvollständiges Paket behandeln..
    }
    std::memcpy(&dataId, position, sizeof(dataId));
    position += sizeof(dataId);
    
    //nächster Wert genauso..
    

    J.Wayne schrieb:

    TyRoXx ich hätte da noch eine Frage bzw. eine Bemerkung. Das Auslesen der Zahlenwerte für die ID funktioniert wunderbar in Bereich von 0-127 sobald die Zahl größer ist kommt Unfug heraus. Wie könnte dies behoben werden?

    Keine Ahnung, wie liest du denn aus?



  • Ich dachte memcpy gilt nur string bzw char* :S naja danke dir TyRoXx.
    Wie liest man die Länge und den eingegebenen String aus. Mit Zeiger funktioniert es nicht so ganz!



  • Beobachter01 schrieb:

    Ich dachte memcpy gilt nur string bzw char* :S naja danke dir TyRoXx.
    Wie liest man die Länge und den eingegebenen String aus. Mit Zeiger funktioniert es nicht so ganz!

    Dann zeig mal deinen Ansatz.


Anmelden zum Antworten