gleichzeitiges Lesen und Schreiben auf demselben Socket in C oder C++

Lesezeit: 6 Minuten

Ich implementiere einen einfachen Server, der eine einzelne Verbindung akzeptiert und dann diesen Socket verwendet, um gleichzeitig Nachrichten aus den Lese- und Schreibthreads zu lesen und zu schreiben. Was ist die sichere und einfache Möglichkeit, gleichzeitig von demselben Socket-Deskriptor in c/c++ unter Linux zu lesen und zu schreiben? Ich muss mich nicht um mehrere Threads kümmern, die von demselben Socket lesen und schreiben, da es einen einzigen dedizierten Lese- und einen einzigen dedizierten Schreib-Thread gibt, der in den Socket schreibt.

Ist im obigen Szenario irgendeine Art von Sperrung erforderlich?

Erfordert das obige Szenario einen nicht blockierenden Socket?

Gibt es eine Opensource-Bibliothek, die im obigen Szenario helfen würde?

Benutzeravatar von Tony Delroy
Toni Delroy

Ist im obigen Szenario irgendeine Art von Sperrung erforderlich?

Keiner.

Erfordert das obige Szenario einen nicht blockierenden Socket?

Das bisschen, worüber Sie sich wahrscheinlich Sorgen machen – das read/recv und write/send Threads auf eine bestehende Verbindung – zu tun nicht müssen nicht blockierend sein, wenn Sie froh sind, dass diese Threads dort sitzen und darauf warten, abgeschlossen zu werden. Das ist normalerweise einer der Gründe, warum Sie eher Threads verwenden würden select, epoll, asynchrone Operationenoder io_uring – hält den Code auch einfacher.

Wenn der Thread neue Clients akzeptiert, blockiert er gerne den Aufruf an accept()dann bist du auch dort gut aufgehoben.

Dennoch gibt es ein subtiles Problem mit TCP-Servern, das Sie vielleicht im Hinterkopf behalten sollten … wenn Ihr Programm wächst, um mehrere Clients zu verwalten und einige regelmäßige Haushaltsarbeiten zu erledigen hat. Es ist natürlich und verlockend, a zu verwenden select oder epoll Rufen Sie dann mit einem Timeout auf, um die Lesbarkeit auf dem lauschenden Socket zu prüfen – was auf einen Verbindungsversuch des Clients hinweist accept die Verbindung. Es gibt dort eine Race-Bedingung: Der Client-Verbindungsversuch wurde möglicherweise unterbrochen select() und accept()in welchem ​​Fall accept() wird blockieren, wenn die lauschende Steckdose nicht nicht blockierend ist, und das kann eine rechtzeitige Rückkehr zum verhindern select() Schleife und halten Sie die periodische On-Timeout-Verarbeitung an, bis ein anderer Client eine Verbindung herstellt.

Gibt es eine Opensource-Bibliothek, die im obigen Szenario helfen würde?

Es gibt Hunderte von Bibliotheken zum Schreiben von Basisservern (und nach Lib-Empfehlungen von Drittanbietern zu fragen, ist bei SO kein Thema, daher werde ich nicht darauf eingehen), aber letztendlich ist das, wonach Sie gefragt haben, leicht auf einem bereitgestellten Betriebssystem zu erreichen BSD-Sockets-API oder die Windows-Bastardisierung (“winsock”).

  • +1 für die Erwähnung der Race-Bedingung und der Designoptionen zur Verwendung von Threads vs. select oder poll

    – Jimm

    23. Oktober 2012 um 19:16 Uhr


  • @ Tony D. Guter Vorschlag. Aus Sicht der Verbesserung kann sich seine Architektur ändern, wenn er in Zukunft plant, OpenSSL zu verwenden. Er kann nicht gleichzeitig auf demselben SSL* lesen und schreiben.

    – begeisterter Geek

    19. Mai 2014 um 12:15 Uhr

  • @enthusiasticgeek: interessant – Ich habe noch keine SSL-Programmierung durchgeführt, wusste also nichts davon, aber es lohnt sich auf jeden Fall, daran zu denken. Prost.

    – Toni Delroy

    19. Mai 2014 um 12:35 Uhr

  • „Da gibt es eine Race-Condition: Der Client-Verbindungsversuch ist möglicherweise zwischen select() und accept() abgebrochen, in diesem Fall blockiert accept() AUCH WENN der lauschende Socket nicht nicht-blockierend ist, und das kann eine rechtzeitige Rückkehr zu verhindern select()-Schleife und stoppt die periodische On-Timeout-Verarbeitung, bis ein anderer Client eine Verbindung herstellt.” –> Ist dies eine bessere Art zu schreiben, was Sie hier geschrieben haben?

    – Avenus

    26. Juli 2021 um 10:49 Uhr

  • @ TonyDelroy Ich habe gerade festgestellt, dass ich Ihre ursprüngliche Antwort falsch gelesen habe, danke für die Hilfe.

    – Avenus

    27. Juli 2021 um 12:10 Uhr

Sie müssen sich keine Sorgen machen. Ein Thread-Lesen und ein Thread-Schreiben funktionieren wie erwartet. Sockets sind Vollduplex, sodass Sie lesen können, während Sie schreiben, und umgekehrt. Sie müssten sich Sorgen machen, wenn Sie mehrere Autoren hätten, aber das ist nicht der Fall.

  • Kurze Antwort, gefällt mir. Mit “Sorge um mehrere Autoren” meinen Sie, dass gleichzeitiges Schreiben auf demselben Kanal die Dinge durcheinander bringen wird, oder? Bei mehreren Schreibthreads muss also ein Synchronisationsverfahren angewendet werden.

    – LppEdd

    9. Juli 2020 um 16:14 Uhr


Steckdosen sind BIDIREKTIONAL. Wenn Sie jemals ein Ethernet- oder serielles Kabel seziert oder den Low-Level-Hardware-Verdrahtungsplan dafür gesehen haben, können Sie tatsächlich deutliche Kupferdrähte für die Leitungen „TX“ (Senden) und „RX“ (Empfangen) SEHEN. Die Software zum Senden der Signale, von der Gerätesteuerung bis zu den meisten Betriebssystem-APIs für einen „Socket“, spiegelt dies wider und ist der Hauptunterschied zwischen einem Socket und einer gewöhnlichen Pipe auf den meisten Systemen (z. B. Linux).

Um Steckdosen wirklich optimal nutzen zu können, benötigen Sie:
1) Asynchrone IO-Unterstützung, die IO Completion Ports, epoll() oder ein ähnliches asynchrones Rückruf- oder Ereignissystem verwendet, um „aufzuwachen“, wenn Daten auf dem Socket eingehen. Dies muss dann Ihre ‘ReadData’-API auf der niedrigsten Ebene aufrufen, um die Nachricht von der Socket-Verbindung zu lesen.
2) Eine zweite API, die Low-Level-Schreibvorgänge unterstützt, ein ‘WriteData’ (Senden), das Bytes auf den Socket schiebt und von nichts abhängt, was die ‘ReadData’-Logik benötigt. Denken Sie daran, dass Ihr Senden und Empfangen sogar auf Hardwareebene unabhängig sind, führen Sie also auf dieser Ebene keine Sperrung oder andere Synchronisierung ein.
3) Ein Pool von Socket-IO-Threads, die blind jede Verarbeitung von Daten ausführen, die von einem Socket gelesen oder in einen Socket geschrieben werden.
4) PROTOKOLL-CALLBACK: Ein Callback-Objekt, auf das die Socket-Threads intelligente Zeiger haben. Es behandelt jede PROTOCOL-Schicht – wie das Parsen Ihres Daten-Blobs in eine echte HTTP-Anforderung – die auf der grundlegenden Socket-Verbindung sitzt. Denken Sie daran, dass ein Socket nur eine Datenleitung zwischen Computern ist und die darüber gesendeten Daten oft als eine Reihe von Fragmenten ankommen – die Pakete. Bei Protokollen wie UDP sind die Pakete nicht einmal geordnet. Die ‘ReadData’ und ‘WriteData’ auf niedriger Ebene werden von ihren Threads hierher zurückgerufen, da hier die inhaltsbewusste Datenverarbeitung tatsächlich beginnt.
5) Alle Rückrufe, die der Protokoll-Handler selbst benötigt. Für HTTP packen Sie die rohen Anforderungspuffer in nette Objekte, die Sie an ein echtes Servlet übergeben, das ein nettes Antwortobjekt zurückgeben sollte, das in eine HTTP-Spezifikations-konforme Antwort serialisiert werden kann.

Beachten Sie das grundlegende Muster: Sie müssen das gesamte System grundlegend asynchron machen (eine „Zwiebel von Rückrufen“), wenn Sie die Vorteile der bidirektionalen, asynchronen E/A über Sockets voll ausschöpfen möchten. Die einzige Möglichkeit, gleichzeitig in den Socket zu lesen und zu schreiben, sind Threads, sodass Sie immer noch zwischen einem ‘Writer’- und ‘Reader’-Thread synchronisieren könnten, aber ich würde es nur tun, wenn das Protokoll oder andere Überlegungen mich dazu zwingen würden. Die gute Nachricht ist, dass Sie mit Sockets mit stark asynchroner Verarbeitung eine hervorragende Leistung erzielen können. Die schlechte ist, dass der Aufbau eines solchen Systems auf robuste Weise eine ernsthafte Anstrengung darstellt.

  • ‘bidirektional’ ist nicht ausreichend. Halbduplex ist immer noch bidirektional, aber nicht gleichzeitig. Es muss Vollduplex sein, um die Anforderungen des OP zu erfüllen. TCP/IP ist sowohl Vollduplex als auch bidirektional.

    – Benutzer207421

    29. März 2017 um 0:45 Uhr

  • Vielen Dank für die ausdrückliche Erwähnung der separaten physischen Leitungen. Dies half mir zu verstehen, wie und warum es bidirektional und vollduplex ist.

    – Kapitän Mann

    2. Juli 2019 um 17:41 Uhr

1408470cookie-checkgleichzeitiges Lesen und Schreiben auf demselben Socket in C oder C++

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

Privacy policy