Nicht blockierender Socket mit Abfrage

Lesezeit: 6 Minuten

Vor ein paar Tagen musste ich ein Problem untersuchen, bei dem meine Anwendung eine ungewöhnlich hohe CPU-Auslastung zeigte, wenn sie sich (anscheinend) im Leerlauf befand. Ich habe das Problem bis zu einer Schleife verfolgt, die auf a blockieren sollte recvfrom anrufen, während die Steckdose auf eingestellt war O_NONBLOCK-ing, was zu einem Spinlock führt. Zur Lösung des Problems gab es zwei Möglichkeiten: Socket auf Blocking setzen oder verfügbare Daten auf dem Socket abfragen mit poll oder select. Ich habe mich für Ersteres entschieden, da es einfacher ist. Aber ich frage mich, warum jemand einen nicht blockierenden Socket erstellen und ihn dann separat abfragen würde. Tut eine blockierende Steckdose nicht dasselbe? In welchen Anwendungsfällen würde man eine Kombination aus nicht blockierendem Socket und Abfrage verwenden? Gibt es Vorteile im Allgemeinen?

Verwenden poll() oder select() mit einem nicht blockierenden Dateideskriptor bietet Ihnen zwei Vorteile:

  • Sie können ein Zeitlimit für die Blockierung festlegen;
  • Sie können auf eines von a warten einstellen von Dateideskriptoren nutzbar zu machen.

Wenn Sie nur auf einen einzigen Dateideskriptor (Socket) warten müssen und es Ihnen nichts ausmacht, auf unbestimmte Zeit darauf zu warten, dann ja; Sie können einfach einen blockierenden Anruf verwenden.

Der zweite Vorteil ist wirklich der Killer-Use-Case für select() und Freunde. Das bedeutet, dass Sie mehrere Socket-Verbindungen sowie Standardeingabe und Standardausgabe und möglicherweise Datei-I/O mit einem einzigen Steuerungs-Thread handhaben können.

Ich poste hier, weil die Frage zwar alt ist. Es ist irgendwie in meiner Google-Suche aufgetaucht und wurde definitiv nicht richtig beantwortet.

Die akzeptierte Antwort hebt lediglich zwei Vorteile der Verwendung nicht blockierender Sockets hervor, geht jedoch nicht wirklich ins Detail oder beantwortet die eigentliche Frage.

  • HINWEIS: Leider enthalten die meisten Online-“Tutorials” oder Code-Snippets nur blockierenden Socket-Code, sodass das Wissen über nicht blockierende Sockets weniger verbreitet ist.

Wann würden Sie das eine im Vergleich zum anderen verwenden … im Allgemeinen werden blockierende Sockets nur in Online-Code-Snippets verwendet. In allen (guten) Produktionsanwendungen werden nicht blockierende Buchsen verwendet. Ich bin nicht unwissend, wenn Sie eine Implementierung kennen, die blockierende Sockets verwendet (und sicher ist, dass dies in Kombination mit Threads sehr gut möglich ist) – oder genauer gesagt, die blockierende Sockets in einem einzelnen Thread verwendet – lassen Sie es bitte ich weiß.

Jetzt kann ich Ihnen ein sehr leicht verständliches Beispiel geben, und es gibt noch viele andere da draußen. Nehmen wir das Beispiel eines Gaming-Servers. Spiele schreiten in Ticks fort, in regelmäßigen Intervallen, in denen der Spielstatus fortschreitet, unabhängig davon, ob der Spieler Eingaben (Maus/Tastatur) vornimmt oder nicht, um den Status des Spiels zu ändern. Wenn jetzt Sockets in Multiplayer-Spielen ins Spiel kommen – wenn Sie blockierende Sockets verwenden würden, würde der Spielstatus nicht fortschreiten, es sei denn, die Spieler senden Updates – wenn sie also Internetprobleme haben, würde der Spielstatus niemals konsistent aktualisiert und Änderungen an alle Spieler weitergegeben . Sie würden eine ziemlich abgehackte Erfahrung haben.

Wenn Sie jetzt nicht blockierende Sockets verwenden, können Sie den Gameserver in einem Single-Thread ausführen und den Spielstatus sowie die Sockets aktualisieren, mit einem … sagen wir 50 ms Timeout-Intervall – und Socket-Daten werden nur dann von verbundenen Benutzern gelesen Sie senden tatsächlich etwas und speisen es dann in die Serversimulation ein, verarbeiten es und speisen es in die Spielzustandsberechnung für den nächsten Tick ein.

  • Nicht select und poll das Blockierungsproblem abmildern, indem Sie über eine bestimmte Operation informiert werden wird nicht blockieren wenn Sie gehen, um diese Operation durchzuführen? Deaktivieren diese guten Produktionsanwendungen immer noch die Blockierung?

    – TheBuzzSaw

    7. September 2017 um 18:53 Uhr

  • @TheBuzzSaw Ich bin mir nicht sicher, ob ich die Frage verstehe. Wir schließen hier die Single-Socket-Nutzung aus, Sie haben also eine Reihe von Dateideskriptoren, die Sie pro Aufruf verwenden, und es ist nicht select, sondern der eigentliche recv-Aufruf, der blockiert und auf den Empfang von Daten wartet.

    – Herr Rogers

    7. September 2017 um 20:08 Uhr


  • Ich sage das, wenn ein Programm verwendet select Um über ein Dutzend Sockets zu multiplexen, macht es normalerweise keinen Sinn, diese Sockets in den nicht blockierenden Modus zu versetzen, da der Aufruf an select bestimmt, welche Aktionen nicht blockiert werden. Wenn select kehrt zurück und sagt mir, dass Socket 45 wird nicht auf einen Lesevorgang blockieren, kann ich anrufen recv ohne angst vor block, oder?

    – TheBuzzSaw

    7. September 2017 um 22:10 Uhr

  • @TheBuzzSaw Ich habe das nicht getestet, aber wenn Sie einen blockierenden Socket in Ihrem fd-Set haben, sollte der recv-Aufruf blockieren. Es macht keinen Sinn, blockierende und nicht blockierende Sockets zu mischen. Hast du einen Anwendungsfall dafür? Nicht zu vergessen, dass der Umgang mit Raw-Sockets mit eigenen Problemen verbunden ist. Ein Socket kann zwischen Ihrem select- und recv-Aufruf ungültig gemacht werden – obwohl selten, aber es kommt vor, jetzt abhängig von der Implementierung, wenn es möglich ist, dass Ihr recv-Aufruf auf dem ungültigen Socket für immer blockiert. Es gibt einfach keinen Grund, Sockets zu blockieren, außer für einige sehr spezifische Szenarien.

    – Herr Rogers

    8. September 2017 um 0:33 Uhr

Benutzer-Avatar
Dummy00001

was zu einem Spinlock führt.

Dieser Zustand wird normalerweise als a bezeichnet enge Schleife.

Zur Lösung des Problems gab es zwei Möglichkeiten: Den Socket auf Blocking setzen oder mit poll oder select nach verfügbaren Daten auf dem Socket pollen. Ich habe mich für Ersteres entschieden, da es einfacher ist.

Sind Sie sicher, dass andere Codeteile nicht bereits verwendet werden poll() (oder select()) und erwarten, dass sich der Socket im nicht blockierenden Modus befindet?

Ansonsten, dann ja, der Wechsel in den Sperrmodus ist die einfachste Lösung.

Am besten abwärtskompatible Lösung wäre vor dem Aufruf gewesen recvfrom() benutzen poll() warten, bis der Socket lesbar wird. Auf diese Weise wird sichergestellt, dass andere Teile des Codes genauso funktionieren wie zuvor.

Aber ich frage mich, warum jemand einen nicht blockierenden Socket erstellen und ihn dann separat abfragen würde. Tut eine blockierende Steckdose nicht dasselbe?

Für den Fall von recvfrom() mir ist kein großer unterschied bekannt.

In welchen Anwendungsfällen würde man eine Kombination aus nicht blockierendem Socket und Abfrage verwenden? Gibt es Vorteile im Allgemeinen?

Könnte ein einfacher Codierungsfehler sein. Oder jemand hätte gedacht, dass Recv’ing in Tight Loop die Leistung irgendwie steigern würde.

Benutzer-Avatar
chennaveeraswamy Hiremath

Es ist immer besser, Steckdosen als zu machen nonblocking denn selbst ein blockierender Socket wird manchmal bereit (wenn Daten angekommen sind, aber einen Prüfsummenfehler aufweisen und dieser verworfen wird) – selbst wenn keine Daten zum Lesen vorhanden sind. Also mach es nonblocking, warten Sie auf die Datenverfügbarkeit durch Abfrage und lesen Sie dann. Ich denke, das ist der Hauptvorteil.

1158590cookie-checkNicht blockierender Socket mit Abfrage

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy