Asynchrone Netzwerkprogrammierung



  • Hi,
    ich beschäftige mich gerade etwas mit asynchroner Netzwerkprogrammierung mit C#. Ich habe trotzdem hier gepostet, weil es mir bei meiner Frage eher um ein allgemeines Design geht.
    Bisher habe ich eine einfache asynchrone Kommunikation zwischen Server und Client implementiert. Also der Client schickt eine Anfrage an den Server, der schickt was zurück und der Client verarbeitet das. Soweit so gut.
    Jetzt möchte ich parallel dazu z.B. eine Datei übertragen. Ich weiß allerdings nicht genau, wie ich das clever programmieren kann. Bisher bin ich von "abgestimmter" Kommunikation ausgegangen, d.h. der Client wartet z.B. nach Anfrage x auf y Antworten vom Server etc. Jetzt hätte ich allerdings das Problem, dass die Pakete von der Hauptschleife und der Dateiübertragung verwechselt werden und die Handler dann die falschen Daten haben.

    Ich hoffe ich konnte mein Problem verständlich schildern. Könnt ihr mir Tipps geben?

    Gruß Hazzel



  • Soll die Datenuebertragung vom Server oder vom Client ausgehen? Push ("hier nimm die Datei an") oder Pull ("bitte gib mir die Datei?").

    Im Prinzip musst du eines von diesen beiden machen:
    Push: derjenige, der die Uebertragung ausloest, dem anderen eine Nachricht schicken "lad bitte die und die Datei runter, verbinde dich dazu bei mir auf Port soundso"). Den Socket dazu solltest du zu dem Zeitpunkt schon gestartet haben, und uebergibst ihn dann einfach einem Thread, der die Uebertragung abwickelt.

    Pull: Derjenige, der die Uebertragung ausloest, schickt dem anderen die Nachricht "bitte lass mich diese und jene Datei runterladen". Der Empfaenger antwortet dann mit "gut, liegt auf Port soundso fuer dich bereit". (Alternativ kann der Initiator gleich eine "bitte schick mir diese Daten auf jenen Port" Nachricht verschicken).



  • Die Übertragung sollte in beide Richtungen funktionieren.
    Also einen neuen Socket erstellen? Darauf bin ich irgendwie noch gar nicht gekommen, wäre jedenfalls die einfachste Lösung . Wie sieht es denn mit der Performance aus? Auch wenn es keine "echte" Anwendung ist, würde ich den Server gerne potentiell für viele Benutzer auslegen.



  • Wenn du eine 2. Connection aufbaust dann achte darauf dass diese in der gleichen Richtung aufgebaut wird wie die 1. Connection, und dass du nirgends IPs oder Portnummern rumschickst. Sonst verträgt sich das nicht gut mit Routern/NATs. FTP ist z.B. ein Protokoll welches sich nicht an diese Regeln hält, weswegen auch sogut wie alle Router/NATs eine FTP-Spezialbehandlung drin haben.

    Eine andere Möglichkeit wäre dass du ein eigenes Paketorientiertes Protokoll auf TCP/IP aufsetzt, so kannst du dann mehrere Datenströme "parallel" über eine TCP/IP Verbindung schicken.

    Im einfachsten Fall könnte das ca. so aussehen:

    4 byte (network byte order): stream-id
    4 byte (network byte order): N = paket-länge
    N bytes: paket-daten

    Was besser ist kommt denke ich auf den genauen Anwenungszweck an.



  • Eine andere Möglichkeit wäre dass du ein eigenes Paketorientiertes Protokoll auf TCP/IP aufsetzt, so kannst du dann mehrere Datenströme "parallel" über eine TCP/IP Verbindung schicken.

    Das war auch mein erster Ansatz, ich bin allerdings an einer gescheiten Streamverwaltung gescheitert. Im Endeffekt konnte ich die Receive-Anfragen an verschiedenen Stellen im Programm nicht zuverlässig den entsprechenden ankommenden Paketen zuordnen. Das meinte ich mit "Pakete verwechselt". Gibt es da vielleicht irgendein Pattern oder so, das mir bei der Implementierung hilft?

    Edit zum Verwendungszweck:
    Ich habe die vorläufige Zielsetzung, eine Art Filesharing-Server-Client System für möglichste viele User zu schreiben. Das ganze ist aber ein Hobby-Frickel-Projekt 😉



  • Im Grunde gehts genau so wie hustbaer gesagt hat.

    while (connectionIsUp())
    {
        PacketType t = readNextPacketType();
        switch (t)
        {
             case FILETRANSFER_PACKET:
                   handleFiletransferPacket();
                   break;
             case FOO_PACKET: 
                   handleFooPacket();
                   break;
        }  
    }
    


  • Blue-Tiger schrieb:

    while (connectionIsUp())
    {
        PacketType t = readNextPacketType();
        switch (t)
        {
             case FILETRANSFER_PACKET:
                   handleFiletransferPacket();
                   break;
             case FOO_PACKET: 
                   handleFooPacket();
                   break;
        }  
    }
    

    lol, da wär ich auch noch drauf gekommen. Ich hab es mittlerweile hinbekommen, hatte einen kleinen Denkfehler in meinem Ansatz.
    Ich hätte aber noch eine andere .Net spezifische Frage: Wie gut skalieren die asynchronen Sockets? Macht es Sinn da noch was zu optimieren, wenn ja was/wie?



  • Hazzel schrieb:

    Im Endeffekt konnte ich die Receive-Anfragen an verschiedenen Stellen im Programm nicht zuverlässig den entsprechenden ankommenden Paketen zuordnen. Das meinte ich mit "Pakete verwechselt". Gibt es da vielleicht irgendein Pattern oder so, das mir bei der Implementierung hilft?

    Ja, der Trick ist einfach nur an einer Stelle Pakete zu empfangen, dann hast du auch nur an einer Stelle ein "Recv" und kannst die Daten nicht verwechseln.
    Wenn du zuerst immer ein Paket komplett empfängst und es erst dann an den entsprechenden Handler weiterreichst sollte das relativ einfach hinzubekommen sein.

    Andrerseits ist IMO auch nix dagegen einzuwenden dass man einen 2. Socket aufmacht.


Anmelden zum Antworten