Wie kann ich eine TcpClient-Verbindung ordnungsgemäß und vollständig schließen?

78

Wie kann eine TcpClient-Verbindung korrekt geschlossen oder zurückgesetzt werden? Wir haben Software, die mit Hardware kommuniziert, aber manchmal geht etwas schief und wir müssen nicht mehr mit ihr kommunizieren, bis wir die Software neu starten.

Ich habe versucht, TcpClient.Close () zu erzwingen und es sogar auf null zu setzen, aber das funktioniert nicht. Es funktioniert nur ein vollständiger Neustart der Software.

Vorschläge?


Ich kann das Schlüsselwort using nicht verwenden, da TpcClient nur an einem Ort definiert ist, aber in der gesamten Bibliothek verwendet wird. (Und es gibt immer nur eine Verbindung)

Es ist eine Bibliothek, die die Kommunikation übernimmt. Die Software selbst kann die ResetConnection () -Methode der Controller-Klasse (die die Hardware darstellt) aufrufen.

Es sieht derzeit so aus

if (tcpClient != null)
{
    tcpClient.Close();
    tcpClient = null;
}

Nach dem, was ich hier gelesen habe, sollte ich tcpClient.Dispose () anstelle von "= null" verwenden.

Ich werde es versuchen und sehen, ob es einen Unterschied macht.

TimothyP
quelle

Antworten:

93

Sie müssen den Stream schließen, bevor Sie die Verbindung schließen:

tcpClient.GetStream().Close();
tcpClient.Close();

Durch das Schließen des Clients wird der Stream nicht geschlossen.

Ignacio Soler Garcia
quelle
11
Wenn das Schließen des TcpClient die Verbindung nicht schließt, was zum Teufel schließt es dann? Gibt es einen Grund, den TcpClient zu schließen?
Qwertie
Ich weiß wirklich nicht, Sie sollten sich bei Reflector erkundigen, aber es schließt wahrscheinlich andere verwendete Objekte anstelle des Streams (möglicherweise kann der Stream von verschiedenen Objekten gemeinsam genutzt werden, und deshalb wird er nicht automatisch geschlossen, sondern nur erraten).
Ignacio Soler Garcia
36
Beachten Sie, dass der Fehler ( support.microsoft.com/kb/821625 ), der das Schließen des Streams erforderlich machte, nur in .NET 1.1 und früheren Versionen vorhanden ist. Ich habe den Beispielcode in .NET 4.5 getestet und er funktioniert einwandfrei, ohne den Stream zu schließen.
Anssssss
4
Unter .NET 4.6.1 treten immer noch zeitweise Probleme auf, wenn ich tcpClient.Close () anstelle von tcpClient.Client.Close () oder tcpClient.GetStream () verwende. Close (). Ich habe neulich fast den Verstand verloren, bis ich das gefunden habe.
Mike Marynowski
System.Net.Sockets.TcpClientRufen Sie mit .NET Standards auf Dispose(). Es gibt keine Close()Methode.
NathanAldenSr
26

Da die akzeptierte Antwort veraltet ist und ich in den anderen Antworten dazu nichts sehe, erstelle ich eine neue. In .Net 2 und früheren Versionen mussten Sie den Stream manuell schließen, bevor Sie die Verbindung schließen konnten. Dieser Fehler wurde in allen späteren Versionen von TcpClientin C # behoben. Wie im Dokument der Close-Methode angegeben , Closeschließt ein Aufruf der Methode sowohl die Verbindung als auch den Stream

BEARBEITEN gemäß Microsoft Docs

Die Close-Methode markiert die Instanz als entsorgt und fordert den zugeordneten Socket auf, die TCP-Verbindung zu schließen. Basierend auf der LingerState-Eigenschaft bleibt die TCP-Verbindung möglicherweise einige Zeit nach dem Aufruf der Close-Methode geöffnet, wenn noch Daten gesendet werden müssen. Es wird keine Benachrichtigung bereitgestellt, wenn die zugrunde liegende Verbindung geschlossen wurde.

Das Aufrufen dieser Methode führt schließlich zum Schließen des zugeordneten Sockets und schließt auch den zugeordneten NetworkStream, der zum Senden und Empfangen von Daten verwendet wird, wenn einer erstellt wurde.

John Demetriou
quelle
@Twometer Auf welche Version von .Net zielen Sie ab?
John Demetriou
Es ist .NET 4.6.1
Twometer
Ich rufe TcpClient.Close (), tcpClient.Client.Close (), .Disconnect () auf und es wird nicht geschlossen. Erst wenn ich den Prozess
abbreche
Das Beispiel in dem Artikel, den Sie verlinkt haben, ruft immer noch networkStream.Close();vor dem Aufruf auf tcpClient.Close();. Entweder ist das Beispiel veraltet oder Sie müssen den zugrunde liegenden Stream noch manuell schließen.
Deantwo
@Deantwo Meine Antwort wurde aktualisiert. Es markiert es als bereit zur Entsorgung. Keine Garantie, wann es entsorgt wird.
John Demetriou
22

Verwenden Sie das Wort : using. Eine gute Angewohnheit beim Programmieren.

using (TcpClient tcpClient = new TcpClient())
{
     //operations
     tcpClient.Close();
}
Michał Ziober
quelle
42
Nicht bei allen Modellen anwendbar. Dies ist ideal, wenn Sie die Verbindung vorübergehend innerhalb eines Codeblocks verwenden. Wenn Sie jedoch eine Client-Implementierung haben, die eine Verbindung als Mitglied aufrechterhalten möchte, schlägt diese Methode fehl. Sie werden auch feststellen, dass gemäß der Klassendokumentation der Aufruf von Dispose () auf dem TcpClient den zugrunde liegenden Socket nicht schließt. Verwenden wird nur entsorgen.
Gusdor
1
Dies funktioniert nicht in asynchronen und nicht blockignalen Sockets. Tatsächlich kann dieser Code nicht verwendet werden, wenn Sie die Verbindung irgendwo speichern und verwalten möchten.
Kas
7
@Gusdor Wenn man sich die reflektierte Quelle ansieht, scheint es, dass Dispose (true) Close () auf dem Client aufruft. (.NET 4.0)
dtroy
Muss neu hinzukommen. Ich habe in Bezug auf .net 3.5 gepostet. Gut erkannt!
Gusdor
1
@Dermot, @SteveJobs gut, jetzt, wo die Zukunft mit dem asyncSchlüsselwort hier ist, tut es das ;-). Es wäre sogar kompatibel mit BeginConnect()und EndConnect(), vorausgesetzt, Sie rufen End…innerhalb des usingBlocks an…
Binki
8

Trotz aller entsprechenden usingAnweisungen, Aufrufen Close, exponentieller Back-Off-Logik und Neuerstellung der TcpClienthabe ich immer noch Probleme festgestellt, bei denen die Anwendung die TCP-Verbindung ohne einen Neustart der Anwendung nicht wiederherstellen kann. Es scheitert immer wieder mit einem System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.

Es gibt jedoch eine Option, LingerStatemit der TcpClientdas Problem möglicherweise behoben wurde (möglicherweise erst nach einigen Monaten, da mein eigenes Hardware-Setup nur so oft fehlschlägt!). Siehe MSDN .

// This discards any pending data and Winsock resets the connection.
LingerOption lingerOption = new LingerOption(true, 0);

using (var tcpClient = new TcpClient 
       {SendTimeout = 2000, ReceiveTimeout = 2000, LingerState = lingerOption })
   ...
Ian Mercer
quelle
Hallo! Ich habe genau das gleiche Problem, obwohl der entsprechende Code manchmal zu zufälligen Zeiten auftritt. Hat die LingerOption Ihr Problem gelöst? Ich denke ernsthaft darüber nach, meinen gesamten Code auf UDP zu migrieren, um dieses zeitweise auftretende Problem zu beseitigen
Rafael
@rafael Ich denke, es hat mein Problem gelöst: Ich habe dieses Problem seit dem Posten überhaupt nicht mehr gesehen.
Ian Mercer
6

Schließt eine Socket-Verbindung und ermöglicht die Wiederverwendung des Sockets:

tcpClient.Client.Disconnect(false);
Justin Tanner
quelle
1
Ich bin verwirrt darüber, warum ich dies tun muss, wenn ich die Verbindung später wieder öffnen möchte. Durch einfaches Schließen von Streams und Schließen wird TcpClientdann eine Ausnahme für einen späteren ausgelöst .Connect.
Pep
6
Meinst du nicht Disconnect (wahr)?
WDUK
6

Der richtige Weg, um die Steckdose zu schließen, damit Sie sie wieder öffnen können, ist:

tcpClient.Client.Disconnect(true);

Der Parameter Boolean gibt an, ob Sie den Socket wiederverwenden möchten:

Trennen Sie die Methodenverwendung

Kevin Gigiano
quelle
5

Mit Ausnahme einiger interner Protokollierungen schließen Sie == Entsorgen.

Entsorgen Sie Aufrufe tcpClient.Client.Shutdown (SocketShutdown.Both), aber es frisst alle Fehler. Wenn Sie es direkt aufrufen, erhalten Sie möglicherweise nützliche Informationen zu Ausnahmen.

jyoung
quelle
1

Haben Sie versucht, TcpClient.Dispose () explizit aufzurufen ?

Und sind Sie sicher, dass Sie über TcpClient.Close () und TcpClient.Dispose () ALLE Verbindungen verfügen ?

Chakrit
quelle
Es gibt nur eine Verbindung, und ich habe sie geschlossen, aber nicht entsorgt ... macht das einen Unterschied? (Ich nehme an, es tut es, nur gefragt :-)
TimothyP
TcpClient.Dispose ist geschützt und kann nicht direkt aufgerufen werden.
rufen
@theycallmemorty doc sagt etwas anderes. msdn.microsoft.com/en-us/library/vstudio/…
Chakrit
2
Es ist eine explizite Schnittstellenimplementierung. Es ist also nicht sichtbar, bis TcpClient eingegeben wird, bis es in IDisposable umgewandelt wird, dh((IDisposable)client).Dispose()
Joseph Lennox