Ich stoße ständig auf eine Situation, in der ich eine Reihe von Nachrichten über eine TCP/IP-Verbindung senden muss. Ich habe nie eine gute Lösung für das Design der Nachrichtenklasse gefunden. Ich würde gerne eine Basisklasse für Nachrichten haben, von der alle Nachrichten abgeleitet sind. Da jede Nachricht unterschiedliche Felder haben wird, würde dies es mir ermöglichen, auf die Felder über Member-Variablen oder Methoden zuzugreifen. Etwas in der Art von...
class message_base
{
public:
message_base();
virtual ~message_base();
unsigned int type;
};
class message_control : public message_base
{
public:
message_control();
virtual ~message_control();
unsigned int action;
};
Dadurch kann ich eine message_control erstellen und auf das action-Mitglied zugreifen, um zu lesen und zu schreiben. Ich kann auch die Nachrichten weitergeben, ohne zu viel Code schreiben zu müssen.
Das Problem ergibt sich, wenn ich die Nachrichten senden muss. Wenn ich die Operatoren << und >> überschreibe, kann ich die Nachrichten über eine Variable nacheinander senden. Das Problem bei dieser Lösung ist, dass bei so vielen Aufrufen zum Senden von Daten die Kontextwechsel den Prozessor überlasten werden. Außerdem landet der Streaming-Operator in der Socket-Klasse und nicht in der Nachrichtenklasse, wo ich ihn lieber hätte.
socket& socket::operator<<(message_control& message)
{
sock << type;
sock << action;
}
Wenn ich die Daten in einem Puffer packe, entferne ich mich von C++ und bewege mich mehr in den Bereich von C und verwende großzügig Zeiger und ähnliches. Die Änderung des Codes ist schwierig und fehleranfällig. Außerdem ist der Streaming-Operator immer noch in der Socket-Klasse und nicht in der Nachrichtenklasse.
socket& socket::operator<<(message_control& message)
{
byte* buffer = new byte[sizeof(message.type) + sizeof(message.action)];
memcpy(buffer, message.type, sizeof(message.type));
memcpy(buffer + sizeof(message.type), message.action, sizeof(message.action));
sock.send(buffer);
}
Mein letzter Versuch verwendete eine Zwischenklasse, um das Packen und Entpacken der Elemente in einem Puffer zu handhaben. Die Nachrichten könnten die Operatoren << und >> für die Pufferklasse implementieren, und dann wird die Pufferklasse an die Socket gesendet. Das funktioniert, fühlt sich aber nicht richtig an.
class socket
{
public:
socket();
~socket();
socket& operator<<(buffer& buff);
};
class buffer
{
public:
buffer() {m_buffer = new byte[initial_size];}
~buffer() {delete [] m_buffer;}
buffer& operator<<(unsigned int value);
private:
byte* m_buffer;
};
void message_control::serialize(buffer& buff)
{
buff << type;
buff << action;
}
Ich kann nicht anders, als zu glauben, dass es eine elegante Lösung für dieses Problem gibt. Ich kann kein Designmuster finden, das dem entspricht, was ich zu erreichen versuche. Hat jemand dieses Problem erlebt und eine gute Lösung gefunden, die einen nicht das Gefühl gibt, dass man besser mit guten alten Zeigern und einem Array von Bytes auskommen würde?
Aktualisierung
In meinem ursprünglichen Beitrag habe ich nicht erwähnt, dass ich es in der Regel mit sehr gut definierten Drahtprotokollen zu tun habe. Deshalb benötige ich in der Regel eine eigene Lösung und kann keine der wunderbaren Toolkits verwenden, die für die Kommunikation über eine Netzwerkverbindung verfügbar sind.