.Net Socket不响应远程断开连接?

我正在编写一个小型(C#)客户端应用程序,它使用TCP / IP连接将数据发送到远程服务器。 我正在使用标准的.Net TcpClient对象,并希望从客户端开始保持连接打开,因为我经常将数据包提交给服务器。 但是,服务器可能会关闭连接,在这种情况下,我需要知道在发送下一个数据包之前重新连接。

使用Wireshark,当服务器终止连接时,我可以看到(仅)以下对话框:

server >>> FIN, ACK
ACK <<< client

我看不到的是我的客户端用自己的FIN响应,以完成连接关闭。 结果是我的客户端程序在发送下一个数据包后才发现连接已关闭。

有没有什么办法可以设置TcpClient或它的底层Socket以完成断开连接,并提供一些反馈,以便我的客户端代码知道在发送下一个数据包之前重新连接?

添加以回应下面的评论:我的发送代码非常简单 – 维护TcpClient和NetworkStream成员变量的对象具有包含(基本上)以下内容的成员函数:

 bool sent = false; byte[] buffer = Encoding.UTF8.GetBytes(dataString); while (!sent) { try { m_outStream.Write(buffer, 0, buffer.Length); sent = true; } catch (Exception ex) { if (m_outStream != null) { m_outStream.Dispose(); } m_client = new TcpClient(AddressFamily.InterNetwork); m_client.Connect(ipAddress, ipPort); m_outStream = m_client.GetStream(); } } 

在初始化m_client和m_outStream的情况下,每次只执行一次传递。 然后使用Wireshark,我可以看到服务器发送带有标志FIN, ACK的数据包,客户端用ACK响应。

下次我调用我的函数时,数据通过PSH, ACK发送出去,服务器用RST, ACK响应但不读取输入数据。 客户没有例外。

然后我第二次调用我的函数,并引发exception,导致重新启动连接。

通常,您应该能够在TcpCient实例上使用Connected属性:

看这里:
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.connected.aspx

然而:

由于Connected属性仅反映最近操作时的连接状态,因此您应尝试发送或接收消息以确定当前状态。 消息发送失败后,此属性不再返回true。 请注意,此行为是设计使然。 您无法可靠地测试连接状态,因为在测试和发送/接收之间的时间内,连接可能已丢失。 您的代码应该假定套接字已连接,并正常处理失败的传输。

请尝试以下操作以确保Connected标志保持最新状态:

 var tcpClient = new TcpClient (); tcpClient.Connect(); var stream = tcpClient.GetStream(); // buffer size need to be > 0 int[] buffer = new int[1]; stream.Read(buffer, 0, 0); if(!tcpClient.Connected) // do something 

基于反编译,应该可以从流中读取0个字节,至少在.NET Framework TcpClient中没有检查可以防止这种情况。 但是,从框架中调用实际从网络流中读取的外部代码中可能没有大声说明。

确保在完成后同时Dispose TcpClientStream ,处理TcpClient不会丢弃Stream因此您需要手动执行此操作,然后释放所有资源(GC之后)。

从MSDN TcpClient.Connected属性:
类型:System.Boolean
如果客户端套接字从最近的操作开始连接到远程资源,则为true;否则为false。 否则,是的。

这意味着,您必须将一些数据发送到服务器以检测断开的连接。 从缓冲区读取时,读取不起作用。

请参阅我在相关问题上的答案( https://stackoverflow.com/a/25680975/2505186 ),
链接其他人的答案,其中描述了一种合适的方法来检测连接状态: 如何检查TcpClient连接是否已关闭?

对您很重要:
当服务器执行此操作时,客户端不会自动关闭连接。 连接处于CLOSE_WAIT状态,然后是客户端,而处于服务器端的FIN_WAIT2状态。 请参阅维基百科文章传输控制协议中的相关部分。 使用上面链接的答案中的代码,您可以检测到连接即将关闭。 此外,您可以完成关闭程序,并在需要时重新打开它。

我用于检测连接状态的方法就是这个。

 static class SocketExtensions { ///  /// Extension method to tell if the Socket REALLY is closed ///  ///  ///  public static bool IsConnected(this Socket socket) { try { return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0); } catch (SocketException) { return false; } } } 

当我想关闭连接时,我调用以下内容。 关闭底层流,然后关闭客户端对象。 我将它封装在试图和捕获中以确保试图在每个试图关闭它们。 注意:在这种情况下,PeerStream是NetworkStream(来自Client.GetStream())

 ///  /// Method will disconnect this peer forcefully ///  public void Disconnect() { try { PeerStream.Close(); } catch (Exception ee) { } try { _client.Client.Disconnect(false); } catch (Exception ee) { } } 

我找到了解决眼前问题的部分答案。

虽然我仍然不知道是否可以让我的TcpClient完成断开连接,但我可以使用以下代码可靠地发现套接字是否已断开连接:

 if (m_client.Client.Poll(1000, SelectMode.SelectRead) && (m_client.Client.Available == 0)) { // Connection has gone - reconnect! m_client = new TcpClient(AddressFamily.InterNetwork); m_client.Connect(ipAddress, ipPort); } else { // Connection is good, nothing to do }