如何设置TcpClient的超时时间?

我有一个TcpClient,我用它将数据发送到远程计算机上的侦听器。 远程计算机有时会打开,有时会closures。 正因为如此,TcpClient将无法经常连接。 我想让TcpClient在一秒钟后超时,所以当它无法连接到远程计算机时,并不需要太多的时间。 目前,我使用这个代码为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 } 

这对于处理任务已经足够了。 如果可以的话,它会发送它,如果它不能连接到远程计算机,则会捕获exception。 但是,如果无法连接,则需要十到十五秒才能抛出exception。 我需要它在一秒钟内超时? 我将如何改变时间?

您需要使用TcpClient的asynchronousBeginConnect方法,而不是尝试同步连接,这是构造函数的作用。 像这样的东西:

 var client = new TcpClient(); var result = client.BeginConnect("remotehost", this.Port, null, null); var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1)); if (!success) { throw new Exception("Failed to connect."); } // we have connected client.EndConnect(result); 

从.NET 4.5开始,TcpClient有一个很酷的ConnectAsync方法,我们可以这样使用,所以现在很简单:

 var client = new TcpClient(); if (!client.ConnectAsync("remotehost", remotePort).Wait(1000)) { // connection failure } 

需要注意的一点是BeginConnect调用在超时到期之前可能失败。 如果您尝试本地连接,则可能会发生这种情况。 这里是Jon的代码的修改版本…

  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); 

上面的答案不包括如何干净地处理已经超时的连接。 调用TcpClient.EndConnect,closures成功但超时后的连接,并丢弃TcpClient。

这可能是矫枉过正,但这对我有用。

  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(); } 

另一种select使用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) { ... } 

在NetworkStream上设置ReadTimeout或WriteTimeout属性以进行同步读取/写入。 更新OP的代码:

 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 }