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

时间:2016-01-28 12:32:27

标签: c# sockets error-handling udp

我有一个可以处理多个客户端的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);
        }
    }
}

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

microsoft提供的错误描述:

  

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

1 个答案:

答案 0 :(得分:0)

我已经找到了解决方法,即以下代码:

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