Linux, Sockets, nicht blockierende Verbindung

Lesezeit: 5 Minuten

Benutzer-Avatar
Heldin

Ich möchte eine nicht blockierende Verbindung herstellen. So was:

socket.connect(); // returns immediately

Dafür verwende ich einen anderen Thread, eine Endlosschleife und Linux epoll. So (Pseudocode):

// in another thread
{
  create_non_block_socket();
  connect();

  epoll_create();
  epoll_ctl(); // subscribe socket to all events
  while (true)
  {
    epoll_wait(); // wait a small time(~100 ms)
    check_socket(); // check on EPOLLOUT event
  }
}

Wenn ich einen Server und dann einen Client betreibe, funktioniert alles. Wenn ich zuerst einen Client ausführe, eine Weile warte, einen Server ausführe, stellt der Client keine Verbindung her.

Was mache ich falsch? Vielleicht geht es auch anders?

  • Wenn Sie einen anderen Thread auslösen, um die Verbindung herzustellen, warum tun Sie dies asynchron? Außerdem können Sie auch den Rest der Kommunikation dort einfügen.

    – Martin Jakob

    21. Juli 2013 um 8:27 Uhr

  • Nun, wie geht das ohne Epoll und Nonblocking? Wenn ich einfach connect() aufrufe, wird es blockieren und auf connect warten (habe ich Recht?). Aber wenn ich diesen Verbindungsthread mit dem Hauptthread verbinden möchte, kann ich das nicht tun, da der Verbindungsthread im Blockierungszustand ist. Tut mir leid, wenn ich falsch liege.

    – Heldin

    21. Juli 2013 um 8:38 Uhr

  • Dies ist nicht “asynchron”. Dies ist nicht blockierend.

    – Benutzer207421

    21. Juli 2013 um 9:52 Uhr

Sie sollten die folgenden Schritte für eine asynchrone Verbindung verwenden:

  • Socket erstellen mit socket(..., SOCK_NONBLOCK, ...)
  • Verbindung starten mit connect(fd, ...)
  • wenn der Rückgabewert keiner von beiden ist 0 Noch EINPROGRESSdann Abbruch mit Fehler
  • warte bis fd wird als ausgabebereit gemeldet
  • Überprüfen Sie den Status der Steckdose mit getsockopt(fd, SOL_SOCKET, SO_ERROR, ...)
  • erledigt

Keine Schleifen – es sei denn, Sie möchten damit umgehen EINTR.

Wenn der Client zuerst gestartet wird, sollten Sie den Fehler sehen ECONNREFUSED im letzten Schritt. Schließen Sie in diesem Fall den Socket und beginnen Sie von vorne.

Es ist schwierig zu sagen, was mit Ihrem Code nicht stimmt, ohne weitere Details zu sehen. Ich nehme an, dass Sie bei Fehlern in Ihrem nicht abbrechen check_socket Betrieb.

  • Ich weiß, dass dies ein alter Kommentar ist, aber ich wollte nur darauf hinweisen, dass ich auf das Lesen warten musste, um ETIMEDOUT zu fangen. Dies trat auf, wenn die SYN-Antwort nicht zurückgegeben wurde. Wenn ich nur auf das Schreiben warten würde, würde der Socket aus netstat verschwinden (aus dem SYN_SENT-Zustand), aber ich würde keine Benachrichtigung erhalten, dass der Socket beschreibbar ist, um getsockopt aufzurufen und ETIMEDOUT zu finden. Ich habe auch unmittelbar nach dem Verbinden einen Aufruf an getsockopt hinzugefügt, um zu sehen, ob vor der Abfrage unmittelbare Fehler verfügbar waren.

    – Traumkrieger

    10. Juni 2015 um 15:02 Uhr


  • @DreamWarrior: Das ist seltsam. Schauen Sie sich an verbinden(2) und verbinden(3) und suchen poll. Beide Manpages geben an, dass Sie auf die Anzeige warten sollten, dass die Steckdose ist schreibbar. Können Sie ein Minimalbeispiel angeben, das das unerwartete Verhalten zeigt?

    – nosid

    11. Juni 2015 um 20:02 Uhr


  • @DreamWarrior: Ich kann das von dir beschriebene Problem nicht reproduzieren. Ich habe ein Minimum geschrieben Testprogrammund es meldet korrekt ETIMEDOUT verwenden POLLOUT.

    – nosid

    19. Juni 2015 um 18:57 Uhr

  • Dies ist keine “asynchrone Verbindung”. Dies ist eine nicht blockierend verbinden. Angesichts der Tatsache, dass das Programm genau nichts tut, außer auf Erfolg oder Misserfolg zu warten, ist der Ansatz völlig sinnlos. Sinnvoller wäre es, den Connect im Blocking Mode zu machen und dann kehren Sie für alles, was folgt, zu Nicht-Blockierung zurück, wenn überhaupt.

    – Benutzer207421

    15. Juli 2018 um 9:57 Uhr

  • Wann getsockopt(fd, SOL_SOCKET, SO_ERROR, ...) gibt 0 zurück, mit 0 in so_error bedeutet dies nicht, dass der Socket verbunden ist. Das bedeutet, dass bisher kein Fehler aufgetreten ist. In diesem speziellen Fall müssen Sie anrufen getpeername() und wenn getpeername() gibt 0 zurück, dies bedeutet, dass der Socket verbunden ist. Wenn die Steckdose nicht angeschlossen ist, getpeername() gibt -1 mit ENOTCONN in errno zurück. getsockopt(fd, SOL_SOCKET, SO_ERROR, ...) kann Sie über eine abgelehnte Verbindung informieren, aber nicht über eine verbundene Steckdose. Sie müssen verwenden getpeername() oder andere Mittel, um sicherzustellen, dass die Steckdose angeschlossen ist.

    – Alexandre Fenyo

    12. Dezember 2018 um 19:49 Uhr


Es gibt verschiedene Möglichkeiten, um zu testen, ob eine nicht blockierende Verbindung erfolgreich ist.

  1. Rufen Sie zuerst getpeername() auf, wenn es mit dem Fehler ENOTCONN fehlgeschlagen ist, ist die Verbindung fehlgeschlagen. Rufen Sie dann getsockopt mit SO_ERROR auf, um den anstehenden Fehler auf dem Socket abzurufen
  2. rufen Sie read mit einer Länge von 0 auf. Wenn das Lesen fehlgeschlagen ist, ist die Verbindung fehlgeschlagen, und die Fehlernummer für read gibt an, warum die Verbindung fehlgeschlagen ist; read gibt 0 zurück, wenn die Verbindung erfolgreich ist
  3. Verbindung erneut anrufen; Wenn die Fehlernummer EISCONN lautet, ist die Verbindung bereits hergestellt und die erste Verbindung erfolgreich.

Ref: UNIX-Netzwerkprogrammierung V1

  • Bitte beachten Sie: das read() Manpage sagt: „Wenn zählen ist null, lesen() kann die unten beschriebenen Fehler erkennen. In Abwesenheit von Fehlern, oder wenn lesen() prüft nicht auf Fehler, a lesen() mit einer Zählung von 0 gibt Null zurück und hat keine anderen Auswirkungen.” So, es KANN die Fehler erkennen.

    – VL-80

    23. August 2021 um 1:12 Uhr


DJ Bernstein hat verschiedene Methoden zusammengestellt, um zu überprüfen, ob es sich um eine Asynchronität handelt connect() Anruf erfolgreich oder nicht. Viele dieser Methoden haben auf bestimmten Systemen Nachteile, daher ist das Schreiben von portablem Code dafür unerwartet schwierig. Wenn jemand alle möglichen Methoden und ihre Nachteile lesen möchte, sehen Sie sich dieses Dokument an.

Für diejenigen, die nur die tl;dr-Version wollen, ist der portabelste Weg der folgende:

Sobald das System den Socket als beschreibbar signalisiert, erster Aufruf getpeername() um zu sehen, ob es verbunden ist oder nicht. Wenn dieser Aufruf erfolgreich war, wurde der Socket verbunden und Sie können ihn verwenden. Wenn dieser Anruf mit fehlschlägt ENOTCONN, die Verbindung ist fehlgeschlagen. Um herauszufinden, warum es fehlgeschlagen ist, versuchen Sie, ein Byte aus dem Socket zu lesen read(fd, &ch, 1)was ebenfalls fehlschlagen wird, aber der Fehler, den Sie erhalten, ist der Fehler, von dem Sie gekommen wären connect() wenn es nicht nicht blockierend wäre.

1329640cookie-checkLinux, Sockets, nicht blockierende Verbindung

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

Privacy policy