Ich habe einen TcpClient, mit dem ich Daten an einen Listener auf einem Remotecomputer sende. Der Remotecomputer ist manchmal ein- und manchmal ausgeschaltet. Aus diesem Grund kann der TcpClient häufig keine Verbindung herstellen. Ich möchte, dass der TcpClient nach einer Sekunde eine Zeitüberschreitung aufweist. Daher dauert es nicht lange, bis keine Verbindung zum Remotecomputer hergestellt werden kann. Derzeit verwende ich diesen Code für den TcpClient:
try
{
TcpClient client = new TcpClient("remotehost", this.Port);
client.SendTimeout = 1000;
Byte[] data = System.Text.Encoding.Unicode.GetBytes(this.Message);
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
data = new Byte[512];
Int32 bytes = stream.Read(data, 0, data.Length);
this.Response = System.Text.Encoding.Unicode.GetString(data, 0, bytes);
stream.Close();
client.Close();
FireSentEvent(); //Notifies of success
}
catch (Exception ex)
{
FireFailedEvent(ex); //Notifies of failure
}
Dies funktioniert gut genug, um die Aufgabe zu erledigen. Es sendet es, wenn es kann, und fängt die Ausnahme ab, wenn es keine Verbindung zum Remotecomputer herstellen kann. Wenn jedoch keine Verbindung hergestellt werden kann, dauert es zehn bis fünfzehn Sekunden, um die Ausnahme auszulösen. Ich brauche es, um in ungefähr einer Sekunde eine Auszeit zu nehmen. Wie würde ich die Auszeit ändern?
quelle
TcpClient
keine Synchronisierungsverbindungsfunktion mit einem konfigurierbaren Zeitlimit angeboten wird. Dies ist eine Ihrer vorgeschlagenen Lösungen. Dies ist eine Problemumgehung, um dies zu aktivieren. Ich bin mir nicht sicher, was ich sonst sagen soll, ohne mich zu wiederholen.Ab .NET 4.5 verfügt TcpClient über eine coole ConnectAsync- Methode, die wir wie folgt verwenden können. Das ist jetzt ganz einfach:
var client = new TcpClient(); if (!client.ConnectAsync("remotehost", remotePort).Wait(1000)) { // connection failure }
quelle
Eine weitere Alternative mit https://stackoverflow.com/a/25684549/3975786 :
var timeOut = TimeSpan.FromSeconds(5); var cancellationCompletionSource = new TaskCompletionSource<bool>(); try { using (var cts = new CancellationTokenSource(timeOut)) { using (var client = new TcpClient()) { var task = client.ConnectAsync(hostUri, portNumber); using (cts.Token.Register(() => cancellationCompletionSource.TrySetResult(true))) { if (task != await Task.WhenAny(task, cancellationCompletionSource.Task)) { throw new OperationCanceledException(cts.Token); } } ... } } } catch(OperationCanceledException) { ... }
quelle
Task.Delay
eine Aufgabe zu erstellen, die nach einer bestimmten Zeit abgeschlossen ist, anstatt dieCancellationTokenSource/TaskCompletionSource
für die Bereitstellung der Verzögerung zu verwenden? (Ich habe es versucht und es sperrt, aber ich verstehe nicht warum)Beachten Sie, dass der BeginConnect-Aufruf möglicherweise vor Ablauf des Zeitlimits fehlschlägt. Dies kann passieren, wenn Sie versuchen, eine lokale Verbindung herzustellen. Hier ist eine modifizierte Version von Jons Code ...
var client = new TcpClient(); var result = client.BeginConnect("remotehost", Port, null, null); result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1)); if (!client.Connected) { throw new Exception("Failed to connect."); } // we have connected client.EndConnect(result);
quelle
In den obigen Antworten wird nicht beschrieben, wie mit einer Verbindung, deren Zeitüberschreitung abgelaufen ist, sauber umgegangen werden kann. Aufrufen von TcpClient.EndConnect, Schließen einer Verbindung, die erfolgreich ist, jedoch nach Ablauf des Zeitlimits, und Entsorgen des TcpClient.
Es mag übertrieben sein, aber das funktioniert bei mir.
private class State { public TcpClient Client { get; set; } public bool Success { get; set; } } public TcpClient Connect(string hostName, int port, int timeout) { var client = new TcpClient(); //when the connection completes before the timeout it will cause a race //we want EndConnect to always treat the connection as successful if it wins var state = new State { Client = client, Success = true }; IAsyncResult ar = client.BeginConnect(hostName, port, EndConnect, state); state.Success = ar.AsyncWaitHandle.WaitOne(timeout, false); if (!state.Success || !client.Connected) throw new Exception("Failed to connect."); return client; } void EndConnect(IAsyncResult ar) { var state = (State)ar.AsyncState; TcpClient client = state.Client; try { client.EndConnect(ar); } catch { } if (client.Connected && state.Success) return; client.Close(); }
quelle
Legen Sie die ReadTimeout- oder WriteTimeout-Eigenschaft im NetworkStream für synchrones Lesen / Schreiben fest. Aktualisieren des OP-Codes:
try { TcpClient client = new TcpClient("remotehost", this.Port); Byte[] data = System.Text.Encoding.Unicode.GetBytes(this.Message); NetworkStream stream = client.GetStream(); stream.WriteTimeout = 1000; // <------- 1 second timeout stream.ReadTimeout = 1000; // <------- 1 second timeout stream.Write(data, 0, data.Length); data = new Byte[512]; Int32 bytes = stream.Read(data, 0, data.Length); this.Response = System.Text.Encoding.Unicode.GetString(data, 0, bytes); stream.Close(); client.Close(); FireSentEvent(); //Notifies of success } catch (Exception ex) { // Throws IOException on stream read/write timeout FireFailedEvent(ex); //Notifies of failure }
quelle
Hier ist eine Codeverbesserung basierend auf der mcandal- Lösung. Ausnahmefang für jede aus der
client.ConnectAsync
Aufgabe generierte Ausnahme hinzugefügt (z. B. SocketException, wenn der Server nicht erreichbar ist)var timeOut = TimeSpan.FromSeconds(5); var cancellationCompletionSource = new TaskCompletionSource<bool>(); try { using (var cts = new CancellationTokenSource(timeOut)) { using (var client = new TcpClient()) { var task = client.ConnectAsync(hostUri, portNumber); using (cts.Token.Register(() => cancellationCompletionSource.TrySetResult(true))) { if (task != await Task.WhenAny(task, cancellationCompletionSource.Task)) { throw new OperationCanceledException(cts.Token); } // throw exception inside 'task' (if any) if (task.Exception?.InnerException != null) { throw task.Exception.InnerException; } } ... } } } catch (OperationCanceledException operationCanceledEx) { // connection timeout ... } catch (SocketException socketEx) { ... } catch (Exception ex) { ... }
quelle
Wenn Sie async & await verwenden und eine Zeitüberschreitung ohne Blockierung verwenden möchten, besteht ein alternativer und einfacherer Ansatz aus der Antwort von mcandal darin, die Verbindung in einem Hintergrundthread auszuführen und auf das Ergebnis zu warten. Zum Beispiel:
Task<bool> t = Task.Run(() => client.ConnectAsync(ipAddr, port).Wait(1000)); await t; if (!t.Result) { Console.WriteLine("Connect timed out"); return; // Set/return an error code or throw here. } // Successful Connection - if we get to here.
Weitere Informationen und andere Beispiele finden Sie im Task.Wait MSDN-Artikel .
quelle
Wie Simon Mourier erwähnte, ist es möglich, die
ConnectAsync
TcpClient-MethodeTask
zusätzlich zu verwenden und den Betrieb so schnell wie möglich zu stoppen.Zum Beispiel:
// ... client = new TcpClient(); // Initialization of TcpClient CancellationToken ct = new CancellationToken(); // Required for "*.Task()" method if (client.ConnectAsync(this.ip, this.port).Wait(1000, ct)) // Connect with timeout of 1 second { // ... transfer if (client != null) { client.Close(); // Close the connection and dispose a TcpClient object Console.WriteLine("Success"); ct.ThrowIfCancellationRequested(); // Stop asynchronous operation after successull connection(...and transfer(in needed)) } } else { Console.WriteLine("Connetion timed out"); } // ...
Außerdem würde ich empfehlen, die AsyncTcpClient C # -Bibliothek anhand einiger Beispiele zu überprüfen, z
Server <> Client
.quelle