多客户端 udp 服务器正确处理错误代码 10054

本文关键字:错误代码 10054 正确处理 服务器 客户端 udp | 更新日期: 2023-09-27 18:32:20

我有一个可以处理多个客户端的UDP服务器,现在UDP的主要事情是它连接较少,所以当我收到以下错误时,我感到非常惊讶:

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

很快了解到这是因为我试图发送到已关闭的 IPEndpoint。后来我才知道这是因为网络层会发回一条 ICMP 消息,说端口已关闭,ICMP 消息是抛出错误的原因。现在显然我开始寻找这个问题的解决方案,但是,尽管我在堆栈溢出上发现了许多关于这个问题的问题,但我找不到一个有正确答案的问题。(有些甚至有 0 个答案)。

当我收到此错误时,我将不再收到任何内容,因为我的BeginReceiveFrom方法在引发异常后位于try部分。然后我也把它放在 catch 部分,但这只会导致再次抛出相同的错误。

所以问题来了,一旦抛出错误:"远程主机强行关闭了现有连接",我就不能再使用套接字了(或者在我看来是这样)

我的

问题是:如何处理此异常,以便我的服务器可以继续运行?

这是我的代码:

public void Listen()
{
    if (mDisposing == true)
    {
        throw new ObjectDisposedException(null, "This instance is already disposed");
    }
    if (mListening == false)
    {
        try
        {
            mListening = true;
            ServerEndPoint = new IPEndPoint(ServerAddress, Port);
            mServerSocket = new Socket(ServerAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
            mServerSocket.Bind(ServerEndPoint);
            if (ServerAddress.AddressFamily == AddressFamily.InterNetworkV6)
            {
                OperatingSystem os = Environment.OSVersion;
                Version version = os.Version;
                // NOTE: Windows Vista or higher have one IP stack for IPv4 and IPv6
                // Therefore they can be combined and used as one socket for IPv6
                // The socket must then accept both IPv4 and IPv6 connections.
                if (version.Major > 5)
                {
                    // NOTE: IPV6_V6ONLY socket option is equivalent to 27 in the winsock snippet below
                    // This is available in Framework 4.0. A lower version can implement (SocketOptionName)27
                    mServerSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
                }
            }
            var ipeSender = new IPEndPoint(IPAddress.Any, 0);
            var endPointSender = (EndPoint)ipeSender;
            mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
        }
        catch (Exception exception)
        {
            mListening = false;
            DoError(exception);
        }
    }
    else
    {
        var ipeSender = new IPEndPoint(IPAddress.Any, 0);
        var endPointSender = (EndPoint)ipeSender;
        mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
    }
}
public void Close()
{
    if (mDisposing == true)
    {
        throw new ObjectDisposedException(null, "This instance is already disposed");
    }
    if (mListening == true)
    {
        mListening = false;
        try
        {
            foreach (ClientInformation client in mClients)
            {
                Disconnect(client.ID);
            }
            if (mServerSocket != null)
            {
                mServerSocket.Close();
            }
        }
        catch (Exception exception)
        {
            DoError(exception);
        }
    }
}
private void WaitForData()
{
    if (mListening == true)
    {
        try
        {
            var ipeSender = new IPEndPoint(IPAddress.Any, 0);
            var endPointSender = (EndPoint)ipeSender;
            mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
        }
        catch (Exception exception)
        {
            DoError(exception);
        }
    }
}
private void OnDataReceived(IAsyncResult asyncResult)
{
    if (mListening == true)
    {
        try
        {
            IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint remoteEndPoint = ipeSender;
            int iRx = mServerSocket.EndReceiveFrom(asyncResult, ref remoteEndPoint);
            var clientInfo = new ClientInformation(remoteEndPoint);
            mClients.Add(clientInfo);
            var chars = new byte[iRx];
            Buffer.BlockCopy(mByteData, 0, chars, 0, iRx);
            WaitForData();
            DoReceived(clientInfo, chars);
        }
        catch (Exception exception)
        {
            WaitForData();
            DoError(exception);
        }
    }
}
public void Send(string remoteEndPoint, byte[] data)
{
    if (mListening == true)
    {
        var clientInfo = ActiveConnections.Find(remoteEndPoint);
        if (clientInfo != null)
        {
            try
            {
                lock (LockSend)
                {
                    clientInfo.DataOut = data;
                    mServerSocket.BeginSendTo(
                        clientInfo.DataOut,
                        0,
                        clientInfo.DataOut.Length,
                        SocketFlags.None,
                        clientInfo.RemoteEndPoint,
                        new AsyncCallback(OnDataSent),
                        clientInfo);
                }
            }
            catch (Exception exception)
            {
                DoError(exception);
            }
        }
        else
        {
            mLogger.ErrorFormat("Trying to send to client {0} which does not exist", remoteEndPoint);
        }
    }
}
private void OnDataSent(IAsyncResult asyncResult)
{
    if (mListening == true)
    {
        var clientInfo = (ClientInformation)asyncResult.AsyncState;
        try
        {
            lock (LockSend)
            {
                int iRx = mServerSocket.EndSendTo(asyncResult);
                if (iRx == clientInfo.DataOut.Length)
                {
                    byte[] chars = new byte[iRx];
                    Buffer.BlockCopy(clientInfo.DataOut, 0, chars, 0, iRx);
                    DoSent(clientInfo, chars);
                }
            }
        }
        catch (Exception exception)
        {
            DoError(exception);
        }
    }
}

我很乐意在需要时提供额外的信息,并希望这个问题可以解决。

微软提供的错误描述:

WSAECONNSET 10054 对等方重置连接。现有连接 被远程主机强行关闭。这通常会导致 远程主机上的对等应用程序突然停止,主机 重新引导,主机或远程网络接口被禁用,或者 远程主机使用硬关闭(有关 Setsockopt 的更多信息,请参见 远程套接字上的SO_LINGER选项)。也可能导致此错误 如果由于保持活动活动检测到 一个或多个操作正在进行时失败。操作 正在进行失败,WSAENETRESET。后续操作失败 与WSAECONNRESET。

多客户端 udp 服务器正确处理错误代码 10054

我已经

能够找到解决此问题的方法,即以下代码:

var sioUdpConnectionReset = -1744830452;
var inValue = new byte[] { 0 };
var outValue = new byte[] { 0 };
mServerSocket.IOControl(sioUdpConnectionReset, inValue, outValue);

据我所知,它只是抑制了错误,我必须在我的 Listen 方法中实现它,现在看起来像这样:

public void Listen()
{
    if (mDisposing == true)
    {
        throw new ObjectDisposedException(null, "This instance is already disposed");
    }
    if (mListening == false)
    {
        try
        {
            mListening = true;
            ServerEndPoint = new IPEndPoint(ServerAddress, Port);
            mServerSocket = new Socket(ServerAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
            var sioUdpConnectionReset = -1744830452;
            var inValue = new byte[] { 0 };
            var outValue = new byte[] { 0 };
            mServerSocket.IOControl(sioUdpConnectionReset, inValue, outValue);
            mServerSocket.Bind(ServerEndPoint);
            if (ServerAddress.AddressFamily == AddressFamily.InterNetworkV6)
            {
                OperatingSystem os = Environment.OSVersion;
                Version version = os.Version;
                // NOTE: Windows Vista or higher have one IP stack for IPv4 and IPv6
                // Therefore they can be combined and used as one socket for IPv6
                // The socket must then accept both IPv4 and IPv6 connections.
                if (version.Major > 5)
                {
                    // NOTE: IPV6_V6ONLY socket option is equivalent to 27 in the winsock snippet below
                    // This is available in Framework 4.0. A lower version can implement (SocketOptionName)27
                    mServerSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
                }
            }
            var ipeSender = new IPEndPoint(IPAddress.Any, 0);
            var endPointSender = (EndPoint)ipeSender;
            mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
        }
        catch (Exception exception)
        {
            mListening = false;
            DoError(exception);
        }
    }
    else
    {
        var ipeSender = new IPEndPoint(IPAddress.Any, 0);
        var endPointSender = (EndPoint)ipeSender;
        mServerSocket.BeginReceiveFrom(mByteData, 0, mByteData.Length, SocketFlags.None, ref endPointSender, new AsyncCallback(OnDataReceived), null);
    }
}