nonblocking send() mit select()?



  • Hi!

    Ich schreibe gerade ein Programm, mit dem sich Client und Server über einen Port gleichzeitig in beide Richtungen Dateien senden können sollen. In eine Richtung klappt das schon ganz gut, jedoch gibt es Probleme, sobald eine Datei gesendet und eine Andere zugleich empfangen werden soll.

    Das gesamte Programm (GUI und Netzwerkfunktionalität) läuft in einem Thread und ich habe auch nicht vor weitere Threads zu verwenden, um das Programm nicht unnötig kompliziert zu machen. Für die Netzwerkgeschichte verwende ich daher select(), um zu überprüfen, ob ein recv() Aufruf blocken würde oder nicht, was auch ganz gut funktioniert. Mein Problem liegt nun beim send().
    send() blockt normalerweise nicht. Die Daten werden AFAIK in einen Socketbuffer - welcher vom Betriebssystem bereit gestellt wird - geschrieben. Ruft die Gegenseite recv() auf, werden die Daten aus dem Socketbuffer hingesendet und der Buffer geleert. Sinn dahinter ist wohl die effizientere Datenübertragung, da man Overhead (TCP-Handshakes) vermeiden möchte und viele kleine Sends lieber als ein großes Paket raushauen möchte (Nagle-Algorithmus).

    Tritt nun der Sonderfall auf, dass bei beiden Programmen der Socketbuffer voll ist, wird bei beiden Programmen send() blocken. Da nun von keiner Seite aus mehr recv() aufgerufen wird, (alles läuft ja in einem Thread) hängen beide Programme unendliche Lange fest - eine Deadlock Situation ist eingetreten.
    Nun kann man ja mit select() auch überprüfen, ob ein Socket schreibbar ist, send() also blockieren würde. select() gibt aber auch schon true zurück, wenn nur 1 Byte des Buffers frei ist. Ein send() mit einer größeren Byteanzahl würde also wieder blockieren, was ich auf jeden Fall vermeiden möchte. Gibt es also eine Möglichkeit auszulesen, wie viel Bytes des Socketbuffers frei bzw. belegt sind?



  • Was du machst ist nicht select + non-blocking IO sondern select + blocking IO, was alle möglichen und unmöglichen Probleme verursacht.

    Schalte deine Verbindungen auf non-blocking um, dann bekommst du statt nem blockierenden Aufruf einfach nen Fehler zurück.

    Wie der Errorcode heisst und wie man auf nonblocking umschaltet, ist unterschiedlich.
    Auf Windows schaltest du mit ioctlsocket(s, FIONBIO, &var) um, und der Errorcode (den du über WSAGetLastError() abfragen kannst) im "Fehlerfall" ist WSAEWOULDBLOCK .

    So wie du es macht, also select + blocking IO, da sind Probleme vorprogrammiert.

    Und natürlich mal wieder falsches Forum.
    Liest eigentlich irgendwer die Forenbeschreibung bevor er/sie postet?
    🙄



  • Ok, ich gebe zu, dass ich mich etwas ungünstig ausgedrückt habe. Natürlich benutze ich blockierende Sockets, ich will eben nur mit select() verhindern, dass da jemals was blockiert. Eine andere Möglichkeit als auf nonblicking sockets umzusteigen gibt es nicht? Ich wäre gerne Plattformunabhängig, da finde ich die Lösung mit select() eigentlich ganz angebracht. Ich müsste wie gesagt nur noch rausfinden, wie viel Bytes im "Betriebssystem Socketbuffer" noch frei sind, dann wären alle Probleme aus der Welt.

    Warum eigentlich falsches Forum? Ich programmiere das doch in C++ und hielt das hier für das passenste Forum. Welches Forum wäre für diese Art von Problem den angebracht?



  • Tipp: Standard-C++ kennt keine Netzwerke.



  • Du bist hier falsch weil Standard C++ keine Sockets kennt.

    Welches Forum wäre für diese Art von Problem den angebracht?

    WinAPI wenns Windows spezifisch ist, Linux wenns Linux spezifisch ist, und Rund um die Programmierung wenns "einfach nur Sockets" ist.
    Würde ich mal sagen.

    Bei Boost.ASIO wärst du hier richtig, da die Boost explizit in der Forenbeschreibung steht - andere "externe" Libraries, und seien sie noch so "üblich", nicht.

    Eine andere Möglichkeit als auf nonblicking sockets umzusteigen gibt es nicht?

    Doch klar, einige. z.B. IO completion ports, epoll, kqueue etc. - die sind dann noch schlimmer plattformabhängig.

    Und es gibt die Boost.ASIO, die kann man synchron oder auch asynchron verwenden - synchron + non-blocking wird vermutlich auch gehen. Die kapselt dann die Unterschiede der einzelnen Plattformen weg.

    Ich wäre gerne Plattformunabhängig, da finde ich die Lösung mit select() eigentlich ganz angebracht.

    2-3 Wrapper-Funktionen mit ein paar #ifdefs und das ganze läuft auf Linux und Windows. Für OSX und BSD sind dann vermutlich noch 0.3 bis 0.7 zusätzliche Zeilen nötig.

    Ich müsste wie gesagt nur noch rausfinden, wie viel Bytes im "Betriebssystem Socketbuffer" noch frei sind, dann wären alle Probleme aus der Welt.

    select + blocking IO = der Teufel

    Was wenn das OS beim theoretischen "wie viel Platz ist noch" Aufruf meint dass noch 100 KB Platz sind, ein paar ns später wenn du send() aufrufst sich aber umentschieden hat (warum auch immer), und send() blockiert nun doch?
    Was wenn du von select() ein "OK für accept" bekommst, und ein paar ns später wenn accept() aufgerufen wurde ist das RST vom client schon dahergekommen der jetzt keine Lust mehr hat zu connecten?

    Alles Mist.
    Lass das.



  • Klingt logisch, hast mich überredet. 🙂 Ich werde mich nun in die nonblocking Thematik einlesen. Wrapperfunktionen sollten es in der Tat tun.

    Und falls das hier ein Moderator liest, bitte ich ihn diesen Thread nach "Rund um die Programmierung" zu verschieben. Danke!



  • Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C++ (auch C++0x und C++11) 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.


Anmelden zum Antworten