2016-08-01 6 views
0

Ich würde gerne auf eine langsame Antwort von einem Client mit TcpClient warten, aber eine Zeitüberschreitung nach etwa 20s, egal wie ich es konfiguriere. Dies ist mein Versuch:TcpClient SocketException mit Timeout nach 20s egal

using (var client = new TcpClient { ReceiveTimeout = 9999999, SendTimeout = 9999999 }) 
{ 
    await client.ConnectAsync(ip, port); 
    using (var stream = client.GetStream()) 
    { 
     // Some quick read/writes happen here via the stream with stream.Write() and stream.Read(), successfully. 

     // Now the remote host is calculating something long and will reply if finished. This throws the below exception however instead of waiting for >20s. 
     var bytesRead = await stream.ReadAsync(new byte[8], 0, 8); 
    } 
} 

Die Ausnahme ist ein IOException:

Daten können nicht von der Transportverbindung lesen: Eine Verbindung Versuch schlug fehl, da die verbundene Partei nicht richtig nach einer Zeit ansprachen der Zeit, oder eine bestehende Verbindung fehlgeschlagen, weil verbundenen Host konnte nicht antworten.

..., die ein SocketException innen enthält:

Ein Verbindungsversuch ist fehlgeschlagen, da die verbundene Partei nach einer gewissen Zeit nicht richtig reagiert hat, oder die hergestellte Verbindung schlug fehl, da der verbundene Host ausgefallen ist zu antworten

SocketErrorCode ist TimedOut.

Die 20s seems to be an OS default on Windows aber ist es nicht möglich, es von verwaltetem Code durch Interaktion mit TcpClient zu überschreiben? Oder wie kann ich sonst auf die Antwort warten?

Ich habe auch die alte Art BeginRead - Weg versucht und das gleiche passiert auf EndRead. Das Problem wird auch nicht von Windows-Firewall oder Defender verursacht.

+0

Update: wenn ich versuche, ReadAsync() erneut auszuführen, nachdem es einmal fehlschlägt, bekomme ich "Eine bestehende Verbindung wurde zwangsweise vom Remote-Host geschlossen". Es ist mein Verdacht, dass der Remote-Host das ganze Problem verursacht, aber schon vorher konnte ich kein Anzeichen dafür finden, dass es die Verbindung schließen würde. – Piedone

Antwort

0

Es stellt sich heraus, dass der Remote-Host nicht rechtzeitig reagiert hat, daher das Problem. Lassen Sie mich näher ausführen, und obwohl dies eine Lösung sein wird, die sehr spezifisch für meinen Fall ist, wird es vielleicht auch für andere nützlich sein.

Das eigentliche Problem war kein Timeout per se, als die Ausnahme angegeben ist, sondern das, was bei zukünftigen Read() Anrufen geworfen Ausnahmen gezeigt:

Die remote „Eine bestehende Verbindung von dem Remote-Host gewaltsam geschlossen wurde“ Der Host schloss die Verbindung nicht absichtlich. Was passierte, war, dass es, wenn es langsam reagierte, tatsächlich so beschäftigt war, dass es auch keinen TCP-Verkehr verarbeitete. Während der lokale Host beim Warten auf eine Antwort nicht explizit etwas gesendet hat, war dies immer noch ein Problem: Der lokale Host versuchte, ACKs für frühere Übertragungen des entfernten Hosts zu senden.Da diese nicht zugestellt werden konnten, hat der lokale Host festgestellt, dass der Remote-Host die Verbindung "zwangsweise" geschlossen hat.

Ich habe den Schlüssel vom Blick auf den Verkehr mit Wireshark (immer gut zu versuchen, zu sehen, was unter der Oberfläche ist statt zu raten): Es war offensichtlich, dass, während der Remote-Host beschäftigt war es vollständige Funkstille zeigte. Zur gleichen Zeit zeigte Wireshark die vom lokalen Gastgeber durchgeführten Weiterverbreitungsversuche, was darauf hinweist, dass dies hinter dem Problem steht.

So konnte die Lösung auch nicht auf dem lokalen Host implementiert werden, das Verhalten des Remote-Hosts musste geändert werden.

1

Ich möchte für eine langsame Antwort von einem Client warten

Es ist wichtig zu beachten, dass es die Verbindung, die fehlschlägt. Der Verbindungstimeout dient nur zum Aufbau einer Verbindung, die immer sehr schnell sein sollte. In der Tat akzeptiert das Betriebssystem Verbindungen im Namen von eine Anwendung, so dass Sie buchstäblich nur über eine Paketrundfahrt sprechen. 21 Sekunden sollten reichen.

Sobald die Verbindung hergestellt ist, können Sie einfach die ReceiveTimeout/SendTimeout entfernen und asynchrone Lesevorgänge verwenden, um für immer zu warten.

+0

Lustig genug, ich habe gerade Ihren Blogpost gelesen: http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html :-) (bin versucht, TCP keep-alive zu verwenden oder ein benutzerdefiniertes "keep-alive" Protokoll. Das Problem ist, dass der Async-Lesevorgang fehlschlägt. Die Verbindung ist erfolgreich und während ich diesen Code der Kürze wegen entfernt habe, gibt es auch einige Schreib-/Lesevorgänge zwischen der Verbindung und dem obigen langsamen ReadAsync(). So funktioniert alles gut, bis der Remote-Host> 20s braucht, um Daten erneut zu senden. – Piedone

+0

@Piedone: Ich hasse es zu sagen, aber bist du * absolut sicher * das ist was du siehst? Ich frage, weil es * kein Timeout bei asynchronen Socket-Reads gibt (für die .NET-Socket-Objekte). 'ReceiveTimeout' wird für asynchrone Lesevorgänge ignoriert. Wenn der Server niemals etwas sendet, sollte der Client einfach nur da sitzen und ewig warten. –

+0

Ich bin mir ziemlich sicher, dass das passiert, was ich sehe. Nach einigen erfolgreichen stream.Write() - und stream.Read() -Aufrufen, wenn ReadAsync() Daten schnell von der Remote abruft, funktioniert es; wenn nicht, Timeout (der ganze Code funktioniert richtig, wenn diese Antwort schneller ist als etwa 1s). Vermutlich ist die Verbindung zum Zeitpunkt der Zeitüberschreitung ebenfalls geöffnet: FWIW TcpClient.Connected == true. Auch habe ich gerade gemerkt, dass ich den GetStream() Aufruf nicht in die Probe aufgenommen habe, geklärt. – Piedone