Design einer Netzwerkbibliothek

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
Halan
Beiträge: 73
Registriert: 22.01.2005, 21:47
Benutzertext: programmiersüchtig
Echter Name: Kai Mast
Wohnort: Freak City
Kontaktdaten:

Design einer Netzwerkbibliothek

Beitrag von Halan »

Hey Leute,

wie schon in meinem letzten Post ( GCC und Templates ) geht es um meine Netzwerkbibliothek aber diesmal rein um das Design. Die Idee hinter meinem Framework ist das Protokoll bzw die Implementation des Protokolls komplett hinter einem Interface zu verstecken, dass für alle Implementierungen gleich ist.

Also etwa so:
Bild

Dabei habe ich jetzt zwei Protokolle/Implementierungen einmal für TCP und einmal für UDP. Beide benutzen die normalen C-Sockets und kein Boost::ASIO oder so. INetworkPort sieht zur Zeit so aus:

Code: Alles auswählen

class INetworkPort
{
    public:
        INetworkPort(INetworkCallback* callback_, u16 maxPeers_, u16 portNumber_, bool listening_, bool IPv6_) : callback(callback_), maxPeers(maxPeers_), portNumber(portNumber_), listening(listening_), IPv6(IPv6_)
        {
            callback->onCreate(this);
        }

        virtual ~INetworkPort(void)
        {
            callback->onDestroy();
        }

        //! Gets the callback
        INetworkCallback* getCallback(void)
        {
            return callback;
        }

        //! Gets the used (listening) port number
        u16 getPortNumber(void) const
        {
            return portNumber;
        }

        //! Get the maximum numbers of possible peers
        u16 getMaxPeers(void) const
        {
            return maxPeers;
        }

        //! States if the port is an IPv6 port
        bool isIPv6(void) const
        {
            return IPv6;
        }

        //! Is the port listening for new connections?
        bool isListening(void) const
        {
            return listening;
        }

        //! Initialization function
        virtual bool init() = 0;

        //! Connect to an address
        //! Returns false if already connected
        virtual bool connect(const SNetworkAddress& address) = 0;

        //! The disconnect pendant
        //! Returns false if the peerID is invalid
        virtual bool disconnect(const PeerID id) = 0;

        //! Disconnects all peers
        //! Returns number of peers disconnected
        virtual u16 disconnectAll(void) = 0;

        //! Gets the address (IP/Port) of a peer
        virtual SNetworkAddress getAddress(const PeerID id) = 0;

        //! Get the latency of a peer
        virtual s32 getLatency(const PeerID id)= 0;

        //! Looks up if a certain address is connected
        virtual bool isConnected(const SNetworkAddress& a) = 0;

        //! Gets the Id of a certain address
        virtual PeerID getPeer(const SNetworkAddress& a) = 0;

        //! Sends and receives data
        virtual void work(void) = 0;

        //! Get the protocol used
        virtual EPortType getType() const = 0;

        //! Send data to peer - Reliable or not
        virtual bool send(const PeerID id, const u8 type, const u32 length, const c8* data, const bool reliable) = 0;

        //! Send data to peer (BitStream Version) - Reliable or not
        virtual bool send(const PeerID id, const u8 type, const BitStream& bstream, const bool reliable) = 0;

        //! Send data to address (Raw)
        virtual bool sendRaw(const SNetworkAddress& address, const u16 length, const c8* data) = 0;

        //! Query certain features
        virtual bool hasFeature(EFeatureType type) const = 0;

        //! Returns number of peers connected
        virtual u16 getPeerCount() = 0;

    protected:
        //! The callback used for all events etc
        INetworkCallback* callback;

        //! The amount of connections possible
        u16 maxPeers;

        //! The port used
        u16 portNumber;

        //! Listen Flag
        bool listening;

        //! Is uning IPv6?
        bool IPv6;
};
Aber meine eignetliche Designfrage bezieht sich garnicht auf das grobe/abstrakte Desing sondern auf die Klasse die UDP handlen soll. Funktioniert zur zeit mit einfachen Pakten ganz super. Will da aber noch 2 Features einbauen: 1. Große Pakete (>1kb) 2. Zuverlässiges(Reliable) Versenden

Für die zuverlässigen Pakete mache ich jetzt einfach Folgendes. Ich gebe jedem Paket eine ID (16-bit unsigned integer) und schicke dann ein Paket vom Empfänger mit der ID zurück um zu zeigen dass es angekommen ist. Nach 2*Latenz wird das Paket erneut verschickt wenn keine Antwort kam. Um doppeltes Empfangen zu verhindern speicher ich auf der Empfängerseite die zuletzt empfangenen IDs. Reihenfolge ist mir egal, soll auch egal sein (sonst könnte ich gleich TCP verwenden).

Mein Fragen hier:
1. Ist das vom Ansatz her intelligent?
2. Verhinder damit ja nicht dass der Inhalt des Pakets kaputt ist oder ähnliches. (Teste zurzeit nur ob die Länge korrekt ist, irgendwelche Checksums scheinen mir zu viel Overhead.

Dann bei den großen Paketen mach ich Folgendes. Ich teile ein Paket in Chunks afu die ich dann mit der oberen Methode sende und auf der Empfängerseite wieder zusammen setz.

Mein Fragen hier:
1. Das scheint für mich ein großer Overhead, kann man das irgendwie intelligenter Lösen?
2. Sollte ich ab einer bestimmten größe komprimieren (mit zlib o.ä.)?

schonmal ein Danke an euch,
Halan
Dateianhänge
Design.png
Jörg
Establishment
Beiträge: 296
Registriert: 03.12.2005, 13:06
Wohnort: Trondheim
Kontaktdaten:

Re: Design einer Netzwerkbibliothek

Beitrag von Jörg »

Warum versuchst Du es nicht ersteinmal nur mit TCP (Nagle aus, send() mit nur immer einem kompletten Paket, etc.) statt dich gleich in die Welt von packet-loss, retransmissions und congestion zu begeben? Das spart ne Menge Arbeit, und wenn man dann am Projektende noch immer der Meinung ist, UDP reisst die Netzwerk-Performance nach oben, dann kann man noch einmal genau schauen, woran es krankt und wie man das mit UDP eventuell besser hinbekommt. Es gibt ja bekanntlich Spiele mit Millionen Kaeufern, die netzwerktechnisch auf TCP zurueck greifen (nein, nicht nur YAGER ;) ).

Wenn Du mit UDP ein ACK zurueck schickst, in welchen du deine Paketnummer mitgibst, dann kannst Du doch gleich noch ein paar dazu packen. So ist es nicht so schlimm, falls eines dieser ACKs verloren geht. Und wenn es passt, dann nimmst Du die nicht einzeln, sondern als Bereich, z.B. Nr.237 und 21 folgende.

Datenintegrietaet pruefen die darunter liegenden Schichten, da wuerde ich mir keine Sorgen machen. Wenn es dir aber um spoofing geht , dann musst du schon etwas tiefer in die Trick-Kiste greifen, Checksummen bringen es dann nicht. Wieso testest Du die Laenge eigentlich? Der IP-stack verwirft normalerweise Pakete, deren physikalische Laenge nicht mit der in IP-kodierten uebereinstimmt.

Um den Overhead wuerde ich mir auch nicht solche Sorgen machen. Stelle einfach sicher, dass die Pakete, welche am haeufigsten gesendet werden, nicht zu klein und nicht zu gross sind.

Zu guter letzt: UDP-Tests mit ihren Grausamkeiten machen sich nicht gut im lokalen LAN ;) Schau dich auch nach einem Simulator fuer verlustbehaftete Netzwerkverbindungen um...
Halan
Beiträge: 73
Registriert: 22.01.2005, 21:47
Benutzertext: programmiersüchtig
Echter Name: Kai Mast
Wohnort: Freak City
Kontaktdaten:

Re: Design einer Netzwerkbibliothek

Beitrag von Halan »

Jörg hat geschrieben:Warum versuchst Du es nicht ersteinmal nur mit TCP (Nagle aus, send() mit nur immer einem kompletten Paket, etc.) statt dich gleich in die Welt von packet-loss, retransmissions und congestion zu begeben? Das spart ne Menge Arbeit, und wenn man dann am Projektende noch immer der Meinung ist, UDP reisst die Netzwerk-Performance nach oben, dann kann man noch einmal genau schauen, woran es krankt und wie man das mit UDP eventuell besser hinbekommt. Es gibt ja bekanntlich Spiele mit Millionen Kaeufern, die netzwerktechnisch auf TCP zurueck greifen (nein, nicht nur YAGER ;) ).
Glaub das kam falsch rüber. TCP hab ich schon eine funktionierende Implementieriung. Kann die hier auch gerne posten, das ganze soll sowieso unter BSD lizenziert sein.

Der Grund wieso ich UDP über TCP bevorzugen würde ist weil mir erstens die Reihenfolge egal ist und zweitens ich viele Pakte haben werde die wenn sie zu spät kommen unwichtig sind (Positions/Geschwindigkeits-Updates z.B.).
Jörg hat geschrieben:Wenn Du mit UDP ein ACK zurueck schickst, in welchen du deine Paketnummer mitgibst, dann kannst Du doch gleich noch ein paar dazu packen. So ist es nicht so schlimm, falls eines dieser ACKs verloren geht. Und wenn es passt, dann nimmst Du die nicht einzeln, sondern als Bereich, z.B. Nr.237 und 21 folgende.
Ich soll also wenn ich ein Paket erhalte mehrere ACK pakete schicken. Also das *selbe* gleich mehrmals? Hab ich das richtig verstanden?
Jörg hat geschrieben:Datenintegrietaet pruefen die darunter liegenden Schichten, da wuerde ich mir keine Sorgen machen. Wenn es dir aber um spoofing geht , dann musst du schon etwas tiefer in die Trick-Kiste greifen, Checksummen bringen es dann nicht. Wieso testest Du die Laenge eigentlich? Der IP-stack verwirft normalerweise Pakete, deren physikalische Laenge nicht mit der in IP-kodierten uebereinstimmt.
ja, ich hatte nicht wirklich Ahnung was der IP-Stack alles für mich macht. Wenn ein packet also ankommt kann ich davon ausgehen dass es Fehlerfrei ist, solang nicht irgendjemand Fehler erzwungen hat?
Jörg hat geschrieben:Um den Overhead wuerde ich mir auch nicht solche Sorgen machen. Stelle einfach sicher, dass die Pakete, welche am haeufigsten gesendet werden, nicht zu klein und nicht zu gross sind.

Zu guter letzt: UDP-Tests mit ihren Grausamkeiten machen sich nicht gut im lokalen LAN ;) Schau dich auch nach einem Simulator fuer verlustbehaftete Netzwerkverbindungen um...
Ja das war mir irgendwie klar dass meine Testapps natürlich Schwachsinnig sind zurzeit da ich sie im lokalen LAN laufen lasse. Was ist denn der zu erwartende Packet-Loss? WLAN und sowas müsste da ja extreme Verluste erzeugen oder nicht?

Zu guter letzt eine Frage von mir an dich. Ich überleg die ganze Zeit ob du der Jörg bist der Zoidcom geschrieben hat :D

gruß,
Halan
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 578
Registriert: 05.07.2003, 11:17

Re: Design einer Netzwerkbibliothek

Beitrag von Lord Delvin »

Halan hat geschrieben:Der Grund wieso ich UDP über TCP bevorzugen würde ist weil mir erstens die Reihenfolge egal ist und zweitens ich viele Pakte haben werde die wenn sie zu spät kommen unwichtig sind (Positions/Geschwindigkeits-Updates z.B.).
Ich hab mich jetzt zugegebenermaßen noch nicht allzulange mit Netzwerkprogrammierung für Spiele auseinandergesetzt, aber bei dem Konzept das mir gerade vorschwebt wäre es inakzeptabel Positions oder Geschwindigkeitsupdates zu verlieren, weil dann die Spieler nicht mehr in der gleichen Welt spielen würden(also die Synchronisation weg ist).

Gibts da irgend n tolles Konzept wie man trotzdem synchronisieren kann oder überseh ich was? Ich mein du kannst doch kein Spiel spielen wenn nicht alle Spieler die selbe (interne) Welt sehen.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Virus
Beiträge: 38
Registriert: 20.09.2002, 17:28
Kontaktdaten:

Re: Design einer Netzwerkbibliothek

Beitrag von Virus »

Lord Delvin hat geschrieben:Gibts da irgend n tolles Konzept wie man trotzdem synchronisieren kann oder überseh ich was? Ich mein du kannst doch kein Spiel spielen wenn nicht alle Spieler die selbe (interne) Welt sehen.
Na klar geht das. Besser gesagt: es geht gar nicht anders. Da die Latenz zu jedem Spieler unterschiedlich ist, sind auch die Daten, die die Spieler haben, einander unterschiedlich. Oder ein Spieler tätigt eine Aktion - davon wissen die anderen erst etwas später. Deshalb sollte die Latenz ja möglichst gering sein, damit alle doch fast das selbe haben.
Je nach Design kann es deshalb auch verschmerzbar sein, wenn einige Pakete nicht ankommen.
Halan
Beiträge: 73
Registriert: 22.01.2005, 21:47
Benutzertext: programmiersüchtig
Echter Name: Kai Mast
Wohnort: Freak City
Kontaktdaten:

Re: Design einer Netzwerkbibliothek

Beitrag von Halan »

Lord Delvin hat geschrieben:Ich hab mich jetzt zugegebenermaßen noch nicht allzulange mit Netzwerkprogrammierung für Spiele auseinandergesetzt, aber bei dem Konzept das mir gerade vorschwebt wäre es inakzeptabel Positions oder Geschwindigkeitsupdates zu verlieren, weil dann die Spieler nicht mehr in der gleichen Welt spielen würden(also die Synchronisation weg ist).
Naja du schickst ja keine Deltas also Posiitionsänderungen sonderen immer die Absoluten Positionen. Von daher ist es total egal wenn mal ein Update verloren geht Der Client interpoliert solange er keine Updates erhält einfach die Position ( s = v*t hat ja auch die Geschwindigkeit vom Server bekommen ).
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: Design einer Netzwerkbibliothek

Beitrag von odenter »

Vielleicht gibt es hier ja gute Beispiele bzw. Antworten auf deine Fragen.
http://code.google.com/p/lidgren-network/
Antworten