检测关闭的网络连接。

4
我曾写过一些通过TCP通信的小程序。由于其中一个程序关闭了其网络连接,另一个端点无法“注意到”已经断开连接,我遇到了无穷无尽的问题而导致系统崩溃。
我本来期望在关闭后进行I/O操作会抛出某种I/O异常,但实际上程序似乎只是“挂起”,永远等待另一端回复。显然,如果连接关闭,回复永远不会到来。(即使您将其放置约二十分钟,它也不会超时。)
有没有办法可以“强制”远程端“看到”我已经关闭了网络连接?
更新:这里是一些代码...
public sealed class Client
{
  public void Connect(IPAddress target)
  {
    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    socket.Connect(ipAddress, 1177);
    _stream = new NetworkStream(socket);
  }

  public void Disconnect()
  {
    _stream.Close();
  }
}

public sealed class Server
{
  public void Listen()
  {
    var listener = new TcpListener(IPAddress.Any, 1177);
    listener.Start();
    var socket = listener.AcceptSocket();
    _stream = new NetworkStream(socket);
    ...
  }

  public void Disconnect()
  {
    socket.Shutdown(SocketShutdown.Both);
    socket.Disconnect(false);
  }
}

在TCP中,一个进程通常不能永远挂起... TCP发送“alive?”信号来检查另一个节点是否仍处于活动状态。 - Willem Van Onsem
因此我感到困惑。 - MathematicalOrchid
1
你考虑过 TcpClient.LingerState 吗?你是如何使用TCP的?是通过Socket编程还是一些专用库? - Willem Van Onsem
你的程序中有一个循环,在其中接收数据时似乎出现了卡顿。请发布该代码。 - Jon
3个回答

2
当一个应用程序正确关闭套接字时,它会发送一个包含0字节的消息。在某些情况下,您可能会收到一个SocketException,表示出现了问题。在第三种情况下,远程方不再连接(例如拔掉网络电缆),而两个方之间没有任何通信。
如果发生最后一种情况,您将需要向套接字写入数据以便检测到无法再与远程方通信。这就是为什么保持活动机制被发明的原因——它们每隔一段时间检查一次是否仍然能够与另一方通信。
看到您发布的代码:使用NetworkStream时,对其进行的Read操作将返回一个值为0(字节),以指示客户端已关闭连接。
文档中提到了两者:文档 “如果没有可读取的数据,则Read方法返回0。”

“如果远程主机关闭连接,并且已经接收到所有可用数据,则Read方法立即完成并返回零字节。”
但实际上,如果在连接打开时没有可用的数据进行读取,则NetworkStream会阻塞。

1
你的意思是Read()返回零字节而不是抛出异常?哦,天啊... - MathematicalOrchid
@MathematicalOrchid 确切无误 - C.Evenhuis
所以可以推测,ReadByte()也将返回 -1 而不是引发实际异常... - MathematicalOrchid
@MathematicalOrchid,他们必须选择除了异常之外的其他东西,因为正确关闭的套接字不是异常情况;而默认的ReadByte实现在幕后调用Read - C.Evenhuis
1
我希望不必在我的代码中散布数十个相同的检查。 (这就是异常处理被发明的原因...)哦,好吧,我猜这不是你的错,他们设计成这样。 :-( - MathematicalOrchid

1
你正在打开套接字,并将其分配给流。在进程结束时,你关闭了网络流,但没有关闭套接字。
为了使NetworkStream.Close()能够关闭底层套接字,必须在构造函数中将所有权参数设置为true-请参阅MSDN文档http://msdn.microsoft.com/en-us/library/te7e60bx.aspx
这可能导致连接挂起,因为底层套接字没有正确关闭。
更改
_stream = new NetworkStream(socket);

_stream = new NetworkStream(socket, true);

顺便提一下,如果您的小应用程序不需要最大性能,则可以尝试使用TCPClient - http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient%28v=vs.100%29.aspx。这是套接字的包装器,并提供连接状态检查功能。


如果我将流包装在 StreamReaderStreamWriter 对象中,是否需要类似的步骤? - MathematicalOrchid
@MathematicalOrchid StreamReader/StreamWriter 没有套接字构造函数,因此您必须将它们包装在 NetworkStream 中。这仍然会导致上述情况。此外,StreamReader 会一直阻塞,直到它收到数据 - 如果没有数据(连接丢失或其他原因),则可能会超时或无限挂起。您最好的选择可能是使用/实现某种保持活动状态的方法。 - Kami

1

这很有趣,但它似乎在谈论进程死亡或网络链接中断的情况。我正在尝试在一端_显式地_关闭连接,这应该会通知另一端,你会认为... - MathematicalOrchid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接