连接后如何测试TCPClient连接断开?

我一直在争论一个问题整整3天我找不​​到任何解决方案,请帮忙:)
我使用Visual Studio 2010和C#语言。

我有一台像服务器一样工作的设备,它在非常不规则的时间段内发送一些数据(不可能定义任何读取超时)。
我写了一个TCP客户端来连接到该服务器并读取数据。 它工作正常,但是当网络出现问题并且服务器变得不可用时(例如,当我从计算机插入网络电缆时),应用程序需要大约10秒才能“注意到”没有连接到服务器并抛出例外。 (我不知道为什么到了10秒?它定义在哪里?我能改变吗?)
我希望反应更快 – 在连接断开后一秒钟之后说。
然而谷歌搜索答案并没有为我提供任何有效的解决方案。

测试代码如下,我尝试在2个线程上进行:一个是读取数据,第二个是查找连接状态,当它被破坏时应该报警。 它既不适用于TCPClient也不适用于Socket类。 我试图用tcpClient.SendTimeout = 100;读/写一些数据tcpClient.SendTimeout = 100;stream.WriteTimeout = 100; 但它似乎没有用。

 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Threading; namespace TCPClient { class Program { static volatile bool _continue = true; static TcpClient tcpClient; static NetworkStream stream; static void Main(string[] args) { try { //client thread - listen from server Thread tcpListenThread = new Thread(TcpListenThread); tcpListenThread.Start(); //connection checking thread Thread keepAliveThread = new Thread(KeepAliveThread); keepAliveThread.Start(); while (_continue) { if (Console.ReadLine() == "q") { _continue = false; } } keepAliveThread.Join(2000); if (keepAliveThread.IsAlive) { Console.WriteLine("Thread keepAlive has been aborted..."); keepAliveThread.Abort(); } tcpListenThread.Join(2000); if (tcpListenThread.IsAlive) { Console.WriteLine("Listen thread has been aborted..."); tcpListenThread.Abort(); } } catch (Exception ex) { Console.WriteLine("\n" + ex.Message); } Console.WriteLine("\nHit any key to quit..."); Console.Read(); } private static void TcpListenThread() { string server = "172.20.30.40"; int port = 3000; try { using (tcpClient = new TcpClient()) { tcpClient.Connect(server, port); if (tcpClient.Connected) Console.WriteLine("Successfully connected to server"); using (stream = tcpClient.GetStream()) { while (_continue) { Byte[] data = new Byte[1024]; Int32 bytes = stream.Read(data, 0, data.Length); string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes); Console.WriteLine("Received: {0}, responseData); } } } } catch (Exception ex) { Console.WriteLine("Listen thread exception! " + ex.Message); } } private static void KeepAliveThread() { while (_continue) { if (tcpClient != null) { try { //...what to put here to check or throw exception when server is not available??... } catch (Exception ex) { Console.WriteLine("Disconnected..."); } } Thread.Sleep(1000); //test for connection every 1000ms } } } } 

编辑:
@ carsten的回答:虽然看起来很有希望,但这个解决方案不起作用……
我做了最简单的测试应用程序:

 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Threading; namespace TCPClientTest { class Program { static void Main(string[] args) { try { string server = "172.20.30.40"; int port = 3000; using (TcpClient tcpClient = new TcpClient()) { tcpClient.Connect(server, port); int i = 0; while (true) { // This is how you can determine whether a socket is still connected. bool blockingState = tcpClient.Client.Blocking; try { byte[] tmp = new byte[1]; tcpClient.Client.Blocking = false; tcpClient.Client.Send(tmp, 0, 0); Console.WriteLine("Connected!"); } catch (SocketException e) { // 10035 == WSAEWOULDBLOCK if (e.NativeErrorCode.Equals(10035)) Console.WriteLine("Still Connected, but the Send would block"); else { Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode); } } finally { tcpClient.Client.Blocking = blockingState; } Console.WriteLine("Connected: {0} ({1})", tcpClient.Client.Connected, i++); Thread.Sleep(1000); } } } catch (Exception ex) { Console.WriteLine("Global exception: {0}", ex.Message); } } } } 

结果如下,它显示:

连接的!
连接:真的

加上我的订单号每一秒。 当我断开网络电缆时,开始打印需要8秒钟:

Disonnected:错误代码10054!
连接:错误

所以到8秒我不知道连接丢失了。 看起来ping是最好的选择,但我会测试另一种解决方案。

简单的回答。 你不能。 Tcp的制作方式不允许这样做。 但是,实现此目的的常规方法是以比消息更短的间隔发送ping。 因此,比方说,每当您从服务器收到消息时,您启动一​​个倒计时1分钟的时钟,然后发送“ping”命令(如果您之间没有收到新消息)。 如果您在30秒内未收到对“ping”的响应,则表明连接已断开。

从理论上讲,你应该能够在包级别上执行此操作(tcp发送你应该能够检查的ACK),但是我不知道这是否可能在C#或任何编程语言中,或者如果你需要在固件/硬件中做到这一点。

我认为这是一个经常出现的问题。 这可能就是为什么MSDN文档确实给出了一个很好的答案 – 请参阅Socket.Connected

从那里引用:

Connected属性获取最后一次I / O操作时Socket的连接状态。 当它返回false时,Socket从未连接,或者不再连接。

Connected属性的值反映了最近操作时的连接状态。 如果需要确定连接的当前状态,请进行非阻塞,零字节发送调用。 如果调用成功返回或抛出WAEWOULDBLOCK错误代码(10035),则套接字仍然连接; 否则,套接字不再连接。

使用此示例代码:

 // .Connect throws an exception if unsuccessful client.Connect(anEndPoint); // This is how you can determine whether a socket is still connected. bool blockingState = client.Blocking; try { byte [] tmp = new byte[1]; client.Blocking = false; client.Send(tmp, 0, 0); Console.WriteLine("Connected!"); } catch (SocketException e) { // 10035 == WSAEWOULDBLOCK if (e.NativeErrorCode.Equals(10035)) Console.WriteLine("Still Connected, but the Send would block"); else { Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode); } } finally { client.Blocking = blockingState; } Console.WriteLine("Connected: {0}", client.Connected); 

以及扩展方法的直接动机:

 public static bool IsConnected(this Socket client) { // This is how you can determine whether a socket is still connected. bool blockingState = client.Blocking; try { byte [] tmp = new byte[1]; client.Blocking = false; client.Send(tmp, 0, 0); return true; } catch (SocketException e) { // 10035 == WSAEWOULDBLOCK if (e.NativeErrorCode.Equals(10035)) return true; else { return false; } } finally { client.Blocking = blockingState; } } 

这是一个非常古老的主题,但它是我搜索这个问题时出现的第一个SOpost,我在其他地方找到了一个更有用的解决方案,所以我想我会发布一个链接来帮助其他人:

https://social.msdn.microsoft.com/Forums/en-US/c857cad5-2eb6-4b6c-b0b5-7f4ce320c5cd/c-how-to-determine-if-a-tcpclient-has-been-disconnected?forum= netfxnetcom&教授=所需

ElFenix发布了这个对我有用的答案:

 // Detect if client disconnected if (tcp.Client.Poll(0, SelectMode.SelectRead)) { byte[] buff = new byte[1]; if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0) { // Client disconnected bClosed = true; } } 

以防万一其他人需要简单有效的东西。

这是我提出的代码

  while (true) { string s = null; DateTime readLag = DateTime.Now; try { s = streamIn.ReadLine(); } catch (Exception ex) { SocketException sockEx = ex.InnerException as SocketException; if (sockEx != null) { OnDebug("(" + sockEx.NativeErrorCode + ") Exception = " + ex); } else { OnDebug("Not SockEx :" + ex); } } if (enableLog) OnDebug(s); if (s == null || s == "") { if (readLag.AddSeconds(.9) > DateTime.Now) { break; } } else { CommandParser(s); } } 

每次读取失败但是会阻塞时,我得到的是本机错误10035。

当连接被删除时,我立即得到一个没有数据的返回。

将readLag.AddSeconds()设置为刚好低于读取超时时间会让我非常清楚时间从未过去,而且我没有数据。 哪个不应该发生。

一旦这个标准出现,我就会退出循环并且线程结束。

希望这有助于其他任何人。

从oginal unix实现中有一个名为SO_KEEPALIVE的套接字选项,它具有TCP协议发送偶尔的探测以确保管道的另一端仍在侦听。 它可以通过Socket.IOControl设置探针的频率来设置

如果您键入以下内容,它将是正确的并且正常工作:

 byte[] tmp = new byte[] {0}; ... client.Send(tmp, 1, 0); ...