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

标签 c# sockets error-handling udp

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

An existing connection was forcibly closed by the remote host.



我很快了解到这是因为我试图发送到已关闭的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);
        }
    }
}

我很高兴在需要时提供其他信息,并希望可以解决此问题。

微软提供的错误描述:

WSAECONNRESET 10054 Connection reset by peer. An existing connection was forcibly closed by the remote host. This normally results if the peer application on the remote host is suddenly stopped, the host is rebooted, the host or remote network interface is disabled, or the remote host uses a hard close (see setsockopt for more information on the SO_LINGER option on the remote socket). This error may also result if a connection was broken due to keep-alive activity detecting a failure while one or more operations are in progress. Operations that were in progress fail with WSAENETRESET. Subsequent operations fail with WSAECONNRESET.

最佳答案

我已经能够找到对此的修复,即以下代码:

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);
    }
}

关于c# - 多客户端udp服务器正确处理错误代码10054,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35061596/

相关文章:

c# - 通过 TCP/IP 连接发送时音频不好

c - unix网络编程select函数总是返回1(resoved)

php - fgets()在socket_select之后返回空字符串

c# - 如何在参数化查询中传递 guid?

c# - 在 .Net dll 中嵌入 git commit hash

c# - 非线性余光层

c# - 根据列名列表将数据表拆分为多个数据表

在 C 中创建一个新字符串以在大型库 (GLib) 中使用,而不使用 fprintf

python-3.x - 未提取SQLAlchemy OperationalError

ruby-on-rails - 在 Builder::XmlMarkup 中处理异常