C# UDP 一个现有的连接被远程主机强行关闭
Posted
技术标签:
【中文标题】C# UDP 一个现有的连接被远程主机强行关闭【英文标题】:C# UDP An existing connection was forcibly closed by the remote host 【发布时间】:2016-11-06 14:44:05 【问题描述】:我正在为一个游戏创建一个服务器,该服务器使用异步方法通过UDP
处理多个客户端,并且正在专门研究干净的断开逻辑。当客户端硬崩溃(他们的程序在没有适当的断开逻辑的情况下关闭)时,服务器上的readCallback
会抛出 SocketException
一个现有的连接被远程主机强行关闭
这是有道理的,但是当下次在read
的循环中触发读取时,尽管在回调中处理了异常,它还是会崩溃。
private void connectedState()
while (connected)
//reset the trigger to non-signaled
readDone.Reset();
read(socket);
//block on reading data
readDone.WaitOne();
private void read(Socket sock)
// Creates an IpEndPoint to capture the identity of the sending host.
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint senderRemote = sender;
// Create the state object.
StateObject state = new StateObject();
state.workSocket = sock;
//crashes after an exception is caught within the callback
sock.BeginReceiveFrom(state.buffer, 0, StateObject.MESSAGE_SIZE, SocketFlags.None, ref senderRemote, new AsyncCallback(readCallback), state);
private void readCallback(IAsyncResult ar)
StateObject state = (StateObject)ar.AsyncState;
Socket sock = state.workSocket;
EndPoint senderRemote = new IPEndPoint(IPAddress.Any, 0);
try
// Read data from the client socket.
int bytesRead = sock.EndReceiveFrom(ar, ref senderRemote);
if (bytesRead <= 0)
//handle disconnect logic
else
//handle the message received
catch (SocketException se)
Console.WriteLine(se.ToString());
// Signal the read thread to continue
readDone.Set();
抛出了两个异常,我认为其中一个被捕获了:
抛出异常:System.dll 中的“System.Net.Sockets.SocketException” System.Net.Sockets.SocketException (0x80004005):现有连接被远程主机强行关闭 在 System.Net.Sockets.Socket.EndReceiveFrom(IAsyncResult asyncResult, EndPoint& endPoint) 在 C:\Users\kayas\Desktop\Practicum\Source\CardCatacombs\CardCatacombs\Utilities\Networking\UDPNetworkConnection.cs:line 424 中的 CardCatacombs.Utilities.Networking.UDPNetworkConnection.readCallback(IAsyncResult ar) 处
抛出异常:System.dll 中的“System.Net.Sockets.SocketException” System.Net.Sockets.SocketException (0x80004005):现有连接被远程主机强行关闭 在 System.Net.Sockets.Socket.DoBeginReceiveFrom(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, EndPoint endPointSnapshot, SocketAddress socketAddress, OverlappedAsyncResult asyncResult)
我希望能够干净地处理客户端崩溃并继续运行,因为还有其他客户端连接到服务器。
【问题讨论】:
不清楚代码在哪里崩溃。请澄清并发布例外情况。 Socket IO 没有特殊的异常规则。异常总是以相同的方式工作并且会被捕获。 请注意,您使用异步 IO 没有任何意义。由于您正在阻止事件,因此您会遇到两全其美的情况:回调和有限的可扩展性。使用sync IO,不要盲目复制不好的示例代码。 查看您的编辑:您仍然需要提供异常,以明确它发生的确切位置。另外,你所说的“崩溃”是什么意思?我应该马上问的。 我更新了帖子以包含引发的异常,并澄清了我所说的崩溃的意思。希望这会有所帮助,尽管示例代码显然没有显示行号 我对 APM 模式不是很熟悉。也许您还需要尝试保护 Begin 调用。你正确地保护了 End 调用。当然“句柄断开逻辑”逻辑也必须出现在catch中。如果没有更完整的代码和对症状的更好描述,很难说发生了什么。崩溃是否意味着控制台应用程序在没有警告的情况下退出? 【参考方案1】:从this forum thread看来,UDP套接字似乎也在接收ICMP消息并在收到时抛出异常。如果端口不再监听(在硬崩溃之后),ICMP 消息会导致“强制关闭”异常。
如果不需要,可以在创建 UdpClient 时使用以下代码禁用此异常,在上面的帖子中进行了解释:
public const int SIO_UDP_CONNRESET = -1744830452;
var client = new UdpClient(endpoint);
client.Client.IOControl(
(IOControlCode)SIO_UDP_CONNRESET,
new byte[] 0, 0, 0, 0 ,
null
);
对于dotnet core用户,由于这个“Socket.IOControl”是windows特有的控制代码,其他平台不支持,会出现如下异常:
未处理的异常。 System.PlatformNotSupportedException:Socket.IOControl 处理特定于 Windows 的控制代码,在此平台上不受支持。
为了更好的兼容性,我们应该检查当前平台:
using System.Runtime.InterosServices;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
//...
【讨论】:
感谢dotnet核心用户,此“Socket.IOControl”是Windows专用控制代码,不支持其他平台。 这救了我 :) 如果你像我一样直接使用套接字,而不是 UdpClient,你可以使用 socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);常量 int SIO_UDP_CONNRESET = -1744830452; socket.IOControl((IOControlCode)SIO_UDP_CONNRESET, new byte[] 0, 0, 0, 0 , null); socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.PacketInformation, true); IPEndPoint server = new IPEndPoint(IPAddress.Any, 30000); socket.Bind(server);以上是关于C# UDP 一个现有的连接被远程主机强行关闭的主要内容,如果未能解决你的问题,请参考以下文章
C# HttpClient 一个现有的连接被远程主机强行关闭
.Net 核心 HttpClient 错误? SocketException:一个现有的连接被远程主机强行关闭