Angenommen, ich verwende einen einfachen Server und habe accept()
eine Verbindung von einem Client hergestellt.
Was ist der beste Weg, um festzustellen, wann der Client die Verbindung getrennt hat? Normalerweise soll ein Client einen Schließbefehl senden, aber was ist, wenn er die Verbindung manuell trennt oder die Netzwerkverbindung insgesamt verliert? Wie kann der Server dies erkennen oder handhaben?
Antworten:
select (mit eingestellter Lesemaske) kehrt mit dem signalisierten Handle zurück. Wenn Sie jedoch ioctl * verwenden, um die Anzahl der zu lesenden Bytes zu überprüfen, ist sie Null. Dies ist ein Zeichen dafür, dass die Steckdose getrennt wurde.
Dies ist eine großartige Diskussion über die verschiedenen Methoden zum Überprüfen, ob der Client die Verbindung getrennt hat: Stephen Cleary, Erkennung von halboffenen (unterbrochenen) Verbindungen .
* Für Windows verwenden Sie ioctlsocket.
quelle
In TCP gibt es nur eine Möglichkeit, eine ordnungsgemäße Trennung zu erkennen, nämlich Null als Rückgabewert
read()/recv()/recvXXX()
beim Lesen zu erhalten.Es gibt auch nur einen zuverlässigen Weg, um eine unterbrochene Verbindung zu erkennen: indem Sie darauf schreiben. Nach genügend Schreibvorgängen in eine unterbrochene Verbindung hat TCP genügend Wiederholungsversuche und Zeitüberschreitungen durchgeführt, um zu wissen, dass die Verbindung unterbrochen wurde, und führt schließlich dazu
write()/send()/sendXXX()
, dass -1 mit demerrno/WSAGetLastError()
WertECONNRESET,
oder in einigen Fällen "Verbindungszeitüberschreitung" zurückgegeben wird. Beachten Sie, dass sich letzteres von dem Verbindungszeitlimit unterscheidet, das in der Verbindungsphase auftreten kann.Sie sollten auch ein angemessenes Lesezeitlimit festlegen und Verbindungen trennen, bei denen dies fehlschlägt.
Die Antwort hier über
ioctl()
undFIONREAD
ist konkurrierender Unsinn. Sie erfahren lediglich, wie viele Bytes sich derzeit im Socket-Empfangspuffer befinden und ohne Blockierung gelesen werden können. Wenn ein Client Ihnen fünf Minuten lang nichts sendet, stellt dies keine Unterbrechung dar, führt jedochFIONREAD
zu einer Null. Nicht dasselbe: nicht einmal in der Nähe.quelle
Um dies etwas näher zu erläutern:
Wenn Sie einen Server ausführen, müssen Sie entweder TCP_KEEPALIVE verwenden, um die Clientverbindungen zu überwachen, oder etwas Ähnliches selbst tun oder Kenntnisse über die Daten / Protokolle haben, die Sie über die Verbindung ausführen.
Wenn die Verbindung unterbrochen wird (dh nicht ordnungsgemäß geschlossen wird), bemerkt der Server dies erst, wenn er versucht, etwas an den Client zu schreiben, was das Keepalive für Sie erreicht. Wenn Sie das Protokoll besser kennen, können Sie alternativ auch bei einem Inaktivitäts-Timeout die Verbindung trennen.
quelle
Timeout
so niedrig ist, ist kein guter Rat ...Wenn Sie überlappende (dh asynchrone) E / A mit Abschlussroutinen oder Abschlussports verwenden, werden Sie sofort benachrichtigt (vorausgesetzt, Sie haben einen ausstehenden Lesevorgang), wenn die Clientseite die Verbindung schließt.
quelle
Suchen Sie nach EPOLLHUP oder EPOLLERR. Wie überprüfe ich, ob die Clientverbindung noch besteht?
Das Lesen und Suchen nach 0 funktioniert in einigen Fällen, aber nicht in allen.
quelle
TCP hat "offene" und "geschlossene" Prozeduren im Protokoll. Nach dem "Öffnen" wird eine Verbindung bis zum "Schließen" gehalten. Es gibt jedoch viele Dinge, die den Datenfluss abnormal stoppen können. Abgesehen davon hängen die Techniken zur Bestimmung, ob es möglich ist, eine Verbindung zu verwenden, stark von den Softwareschichten zwischen dem Protokoll und dem Anwendungsprogramm ab. Die oben genannten konzentrieren sich auf einen Programmierer, der versucht, einen Socket nicht-invasiv zu verwenden (0 Bytes lesen oder schreiben), sind möglicherweise am häufigsten. Einige Ebenen in Bibliotheken liefern die "Abfrage" für einen Programmierer. Zum Beispiel können Win32-Asych-Aufrufe (verzögert) einen Lesevorgang starten, der fehlerfrei und mit 0 Bytes zurückgegeben wird, um einen Socket zu signalisieren, der nicht mehr gelesen werden kann (vermutlich eine TCP-FIN-Prozedur). Andere Umgebungen verwenden möglicherweise "Ereignisse" wie in ihren Wickelschichten definiert. Es gibt keine einheitliche Antwort auf diese Frage. Der Mechanismus zum Erkennen, wann ein Socket nicht verwendet werden kann und geschlossen werden sollte, hängt von den in den Bibliotheken bereitgestellten Wrappern ab. Es ist auch erwähnenswert, dass Sockets selbst von Ebenen unterhalb einer Anwendungsbibliothek wiederverwendet werden können. Es ist daher ratsam, herauszufinden, wie Ihre Umgebung mit der Berkley Sockets-Oberfläche umgeht.
quelle
""" tcp_disconnect.py Echo network data test program in python. This easily translates to C & Java. A server program might want to confirm that a tcp client is still connected before it sends a data. That is, detect if its connected without reading from socket. This will demonstrate how to detect a TCP client disconnect without reading data. The method to do this: 1) select on socket as poll (no wait) 2) if no recv data waiting, then client still connected 3) if recv data waiting, the read one char using PEEK flag 4) if PEEK data len=0, then client has disconnected, otherwise its connected. Note, the peek flag will read data without removing it from tcp queue. To see it in action: 0) run this program on one computer 1) from another computer, connect via telnet port 12345, 2) type a line of data 3) wait to see it echo, 4) type another line, 5) disconnect quickly, 6) watch the program will detect the disconnect and exit. John Masinter, 17-Dec-2008 """ import socket import time import select HOST = '' # all local interfaces PORT = 12345 # port to listen # listen for new TCP connections s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((HOST, PORT)) s.listen(1) # accept new conneciton conn, addr = s.accept() print 'Connected by', addr # loop reading/echoing, until client disconnects try: conn.send("Send me data, and I will echo it back after a short delay.\n") while 1: data = conn.recv(1024) # recv all data queued if not data: break # client disconnected time.sleep(3) # simulate time consuming work # below will detect if client disconnects during sleep r, w, e = select.select([conn], [], [], 0) # more data waiting? print "select: r=%s w=%s e=%s" % (r,w,e) # debug output to command line if r: # yes, data avail to read. t = conn.recv(1024, socket.MSG_PEEK) # read without remove from queue print "peek: len=%d, data=%s" % (len(t),t) # debug output if len(t)==0: # length of data peeked 0? print "Client disconnected." # client disconnected break # quit program conn.send("-->"+data) # echo only if still connected finally: conn.close()
quelle
In Python können Sie eine Try-Except-Anweisung wie folgt ausführen:
try: conn.send("{you can send anything to check connection}") except BrokenPipeError: print("Client has Disconnected")
Dies funktioniert, da Python beim Schließen des Programms durch den Client / Server einen fehlerhaften Pip-Fehler an den Server oder Client zurückgibt, je nachdem, wer die Verbindung getrennt hat.
quelle
Es ist wirklich einfach: zuverlässig und nicht chaotisch:
quelle
Ich habe mit ein paar Lösungen gespielt, aber diese scheint am besten zu funktionieren, um die Trennung von Host und / oder Client unter Windows zu erkennen. Es ist für nicht blockierende Sockets vorgesehen und aus dem Beispiel von IBM abgeleitet .
char buf; int length=recv(socket, &buf, 0, 0); int nError=WSAGetLastError(); if(nError!=WSAEWOULDBLOCK&&nError!=0){ return 0; } if (nError==0){ if (length==0) return 0; }
quelle
Der Rückgabewert des Empfangs ist -1, wenn die Verbindung unterbrochen wird, andernfalls hat er die Größe des Puffers.
void ReceiveStream(void *threadid) { while(true) { while(ch==0) { char buffer[1024]; int newData; newData = recv(thisSocket, buffer, sizeof(buffer), 0); if(newData>=0) { std::cout << buffer << std::endl; } else { std::cout << "Client disconnected" << std::endl; if (thisSocket) { #ifdef WIN32 closesocket(thisSocket); WSACleanup(); #endif #ifdef LINUX close(thisSocket); #endif } break; } } ch = 1; StartSocket(); } }
quelle
errno == ECONNRESET
und es wird überhaupt nichts mit dem Puffer tun.