wake-up-neo.com

Wie Sie feststellen können, ob eine Verbindung unterbrochen ist python

Ich möchte, dass meine python - Anwendung erkennt, wann der Socket auf der anderen Seite gelöscht wurde. Gibt es eine Methode dafür?

57
directedition

Es kommt darauf an, was Sie mit "fallen gelassen" meinen. Wenn für TCP Sockets das andere Ende die Verbindung entweder durch close () oder durch Beenden des Prozesses schließt, können Sie dies herausfinden, indem Sie ein Dateiende lesen oder einen Lesefehler erhalten, in der Regel den Errno wird von Ihrem Betriebssystem auf die von Peer zurückgesetzte Verbindung gesetzt. Bei Python lesen Sie eine Zeichenfolge mit der Länge Null, oder es wird ein Socket-Fehler ausgegeben, wenn Sie versuchen, vom Socket zu lesen oder zu schreiben.

36
Andy V

Kurze Antwort:

verwenden Sie ein nicht blockierendes recv () oder ein blockierendes recv ()/select () mit einem sehr kurzen Timeout.

Lange Antwort:

Die Methode zum Behandeln von Socket-Verbindungen besteht darin, nach Bedarf zu lesen oder zu schreiben und auf Verbindungsfehler vorbereitet zu sein.

TCP unterscheidet drei Arten, eine Verbindung zu "trennen": Zeitüberschreitung, Zurücksetzen, Schließen.

Von diesen kann die Zeitüberschreitung nicht wirklich erkannt werden. TCP könnte Ihnen nur sagen, dass die Zeit noch nicht abgelaufen ist. Aber selbst wenn es Ihnen gesagt hat, dass die Zeit noch unmittelbar danach abläuft.

Denken Sie auch daran, dass mit shutdown () entweder Sie oder Ihr Peer (das andere Ende der Verbindung) möglicherweise nur den eingehenden Bytestream schließen und den ausgehenden Bytestream weiter ausführen oder den ausgehenden Stream schließen und den eingehenden Stream weiter ausführen.

Genau genommen möchten Sie überprüfen, ob der Lesestream geschlossen ist oder ob der Schreibstrom geschlossen ist oder ob beide geschlossen sind.

Auch wenn die Verbindung "getrennt" wurde, sollten Sie weiterhin in der Lage sein, alle Daten zu lesen, die sich noch im Netzwerkpuffer befinden. Erst wenn der Puffer leer ist, wird die Verbindung zu recv () getrennt.

Wenn Sie prüfen, ob die Verbindung getrennt wurde, werden Sie gefragt: "Was erhalte ich nach dem Lesen aller Daten, die derzeit gepuffert sind?". Um das herauszufinden, müssen Sie nur alle Daten lesen, die aktuell gepuffert sind.

Ich kann sehen, wie das "Lesen aller gepufferten Daten" für einige Leute, die recv () immer noch als blockierende Funktion betrachten, ein Problem sein kann, um an das Ende zu gelangen. Mit einem blockierenden recv () blockiert die "Prüfung" für einen Lesevorgang, wenn der Puffer bereits leer ist, was den Zweck der "Prüfung" zunichte macht.

Meiner Meinung nach ist jede Funktion, die dokumentiert ist, um den gesamten Prozess auf unbestimmte Zeit zu blockieren, ein Konstruktionsfehler, aber ich denke, sie ist aus historischen Gründen immer noch vorhanden, da die Verwendung eines Sockets wie eines regulären Dateideskriptors eine coole Idee war.

Was Sie tun können, ist:

  • setzen Sie den Socket in den nicht blockierenden Modus, es wird jedoch ein systemabhängiger Fehler angezeigt, der darauf hinweist, dass der Empfangspuffer leer oder der Sendepuffer voll ist
  • halte dich an den Blockiermodus, aber stelle eine sehr kurze Socket-Zeitüberschreitung ein. Auf diese Weise können Sie den Socket mit recv () "pingen" oder "überprüfen", so ziemlich alles, was Sie tun möchten
  • benutze select () call oder asyncore module mit einem sehr kurzen timeout. Die Fehlermeldung ist weiterhin systemspezifisch.

Für den Write-Teil des Problems wird dies weitgehend dadurch abgedeckt, dass die Lesepuffer leer bleiben. Sie werden feststellen, dass eine Verbindung nach einem nicht blockierenden Leseversuch "getrennt" wurde, und Sie können festlegen, dass keine weiteren Nachrichten gesendet werden, nachdem ein Lesevorgang einen geschlossenen Kanal zurückgegeben hat.

Ich denke, der einzige Weg, um sicherzugehen, dass Ihre gesendeten Daten das andere Ende erreicht haben (und sich noch nicht im Sendepuffer befinden), ist entweder:

  • erhalten Sie eine richtige Antwort auf den gleichen Socket für die genaue Nachricht, die Sie gesendet haben. Grundsätzlich verwenden Sie das übergeordnete Protokoll zur Bestätigung.
  • führe ein erfolgreiches Herunterfahren () durch und schließe () den Socket

Das python Socket-Howto send () gibt 0 geschriebene Bytes zurück, wenn der Kanal geschlossen ist. Sie können ein nicht blockierendes oder ein Timeout-Socket verwenden. Send () und wenn es 0 zurückgibt, können Sie no Sende länger Daten über diesen Socket, aber wenn er nicht Null ist, hast du schon etwas gesendet, viel Glück damit :)

Auch hier habe ich keine OOB-Socket-Daten (Out-of-Band) als Mittel zur Lösung Ihres Problems angesehen, aber ich denke, OOB war nicht das, was Sie meinten.

44
Toughy

Über den Link, den Jweede gepostet hat:

ausnahme socket.timeout:

This exception is raised when a timeout occurs on a socket
which has had timeouts enabled via a prior call to settimeout().
The accompanying value is a string whose value is currently
always “timed out”.

Hier sind die Demo-Server- und Client-Programme für das Socket-Modul aus dem Python-Docs

# Echo server program
import socket

Host = ''                 # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((Host, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
    data = conn.recv(1024)
    if not data: break
    conn.send(data)
conn.close()

Und der Kunde:

# Echo client program
import socket

Host = 'daring.cwi.nl'    # The remote Host
PORT = 50007              # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((Host, PORT))
s.send('Hello, world')
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Auf der Beispielseite der Dokumentation, von der ich diese abgerufen habe, gibt es komplexere Beispiele, die diese Idee verwenden, aber hier ist die einfache Antwort:

Angenommen, Sie schreiben das Client-Programm. Fügen Sie einfach den gesamten Code, der den Socket verwendet, in einen Try-Block ein, wenn die Gefahr besteht, dass er gelöscht wird.

try:
    s.connect((Host, PORT))
    s.send("Hello, World!")
    ...
except socket.timeout:
    # whatever you need to do when the connection is dropped
14
Jiaaro

Ich habe das Codebeispiel in diesem Blogbeitrag in Python übersetzt: Wie erkenne ich, wann der Client die Verbindung schließt? , und es funktioniert gut für mich:

from ctypes import (
    CDLL, c_int, POINTER, Structure, c_void_p, c_size_t,
    c_short, c_ssize_t, c_char, ARRAY
)


__all__ = 'is_remote_alive',


class pollfd(Structure):
    _fields_ = (
        ('fd', c_int),
        ('events', c_short),
        ('revents', c_short),
    )


MSG_DONTWAIT = 0x40
MSG_PEEK = 0x02

EPOLLIN = 0x001
EPOLLPRI = 0x002
EPOLLRDNORM = 0x040

libc = CDLL(None)

recv = libc.recv
recv.restype = c_ssize_t
recv.argtypes = c_int, c_void_p, c_size_t, c_int

poll = libc.poll
poll.restype = c_int
poll.argtypes = POINTER(pollfd), c_int, c_int


class IsRemoteAlive:  # not needed, only for debugging
    def __init__(self, alive, msg):
        self.alive = alive
        self.msg = msg

    def __str__(self):
        return self.msg

    def __repr__(self):
        return 'IsRemoteClosed(%r,%r)' % (self.alive, self.msg)

    def __bool__(self):
        return self.alive


def is_remote_alive(fd):
    fileno = getattr(fd, 'fileno', None)
    if fileno is not None:
        if hasattr(fileno, '__call__'):
            fd = fileno()
        else:
            fd = fileno

    p = pollfd(fd=fd, events=EPOLLIN|EPOLLPRI|EPOLLRDNORM, revents=0)
    result = poll(p, 1, 0)
    if not result:
        return IsRemoteAlive(True, 'empty')

    buf = ARRAY(c_char, 1)()
    result = recv(fd, buf, len(buf), MSG_DONTWAIT|MSG_PEEK)
    if result > 0:
        return IsRemoteAlive(True, 'readable')
    Elif result == 0:
        return IsRemoteAlive(False, 'closed')
    else:
        return IsRemoteAlive(False, 'errored')
5
kay

Wenn ich mich nicht irre, wird dies normalerweise über ein Timeout behandelt.

4
Jon W