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:一个现有的连接被远程主机强行关闭

现有连接被远程主机强行关闭

Windows下的UDP爆了10054--远程主机强迫关闭了一个现有的连接

UDP错误10054:远程主机强迫关闭了一个现有的连接

现有连接被远程主机强行关闭