简体   繁体   中英

C# .NET 4.0/4.5 UDP send issue

I am working on writing a network library in C# and originally had used the .NET 3.5 Framework. I recently decided to switch to .NET 4.5 but started running into an issue with sending UDP packets. What I'm running into is if UDP packets are sent too fast, the Socket.SendToAsync method completes with a SocketError of AddressFamilyNotSupported and the packets are never sent.

If I switch the project to .NET 3.5, I never run into the issue no matter how hard I try to repeat it. This also can be reproduced in .NET 4.0.

Here is a link to the project I put together to reproduce the issue. If you spam the "ClientSnd" or "ServerSnd" buttons you'll see the error occur. Switch the project to .NET 3.5 and spam all you want... no issues at all.

I haven't been able to find much useful information searching on this issue. Any ideas?

EDIT (added code from the sample project demoing the issue):

Here's where the binds are happening for both the client and server:

            byte[] clientBuffer = new byte[32768];
            byte[] serverBuffer = new byte[32768];

            IPEndPoint clientLocalEndPoint = GetLocalIPEndPoint(0, AddressFamily.InterNetwork);
            IPEndPoint serverLocalEndPoint = GetLocalIPEndPoint(6337, AddressFamily.InterNetwork);

            m_ClientSocket.ExclusiveAddressUse = true;
            m_ServerSocket.ExclusiveAddressUse = true;
            m_ClientSocket.Bind(clientLocalEndPoint);
            m_ServerSocket.Bind(serverLocalEndPoint);

            m_ClientSendArgs.RemoteEndPoint = GetRemoteIPEndPoint("127.0.0.1", 6337, AddressFamily.InterNetwork);
            m_ClientRecvArgs.RemoteEndPoint = m_ClientSocket.LocalEndPoint;

            m_ServerSendArgs.RemoteEndPoint = GetRemoteIPEndPoint("127.0.0.1", ((IPEndPoint)m_ClientSocket.LocalEndPoint).Port, AddressFamily.InterNetwork);
            m_ServerRecvArgs.RemoteEndPoint = m_ServerSocket.LocalEndPoint;

            m_ClientSendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnClientCompletion);
            m_ClientRecvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnClientCompletion);
            m_ServerSendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnServerCompletion);
            m_ServerRecvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnServerCompletion);

            m_ClientRecvArgs.SetBuffer(clientBuffer, 0, clientBuffer.Length);
            m_ServerRecvArgs.SetBuffer(serverBuffer, 0, serverBuffer.Length);

            ClientReceive();
            ServerReceive();

The GetRemoteIPEndPoint and GetLocalIPEndPoint methods:

    private static IPEndPoint GetRemoteIPEndPoint(string address, int port, AddressFamily addressFamily)
    {
        IPAddress[] ipAddresses = null;

        ipAddresses = Dns.GetHostAddresses(address);

        List<IPEndPoint> ipEndPointList = new List<IPEndPoint>();

        for (int i = 0; i < ipAddresses.Length; i++)
        {
            IPAddress ipAddress = ipAddresses[i];

            if (ipAddress.AddressFamily == addressFamily)
            {
                IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);

                ipEndPointList.Add(ipEndPoint);
            }
        }

        return ipEndPointList.ToArray()[0];
    }

    private static IPEndPoint GetLocalIPEndPoint(int port, AddressFamily addressFamily)
    {
        IPEndPoint localEndPoint = null;

        switch (addressFamily)
        {
            case AddressFamily.InterNetwork:
                {
                    localEndPoint = new IPEndPoint(IPAddress.Any, port);

                    break;
                }
            case AddressFamily.InterNetworkV6:
                {
                    localEndPoint = new IPEndPoint(IPAddress.IPv6Any, port);

                    break;
                }
        }

        return localEndPoint;
    }

Since this happens regardless of who sends the data (client or server), I'll focus on the client being the sender:

Clicking the ClientSnd button:

    private void Button_ClientSnd_Click(object sender, RoutedEventArgs e)
    {
        lock (SyncRoot)
        {
            byte[] buffer = Encoding.ASCII.GetBytes("Hello there.  Just testing.  Nothing to see here.  Move along.");

            m_ClientSendQueue.Enqueue(buffer);

            if (!m_ClientTransmitting)
            {
                m_ClientTransmitting = true;

                ClientSendBuffer();
            }
        }
    }

Sending methods for the client:

    private void ClientSendBuffer()
    {
        lock (SyncRoot)
        {
            if (m_ClientSendQueue.Count > 0)
            {
                byte[] buffer = m_ClientSendQueue.Dequeue();

                m_ClientSendArgs.SetBuffer(buffer, 0, buffer.Length);

                ClientSend();
            }
            else
            {
                m_ClientTransmitting = false;
            }
        }
    }

    private void ClientSend()
    {
        if (!m_ClientSocket.SendToAsync(m_ClientSendArgs))
        {
            OnClientCompletion(this, m_ClientSendArgs);
        }
    }

Completion callback for the client:

    private void OnClientCompletion(object sender, SocketAsyncEventArgs e)
    {
        SocketError socketError = e.SocketError;

        if (socketError != SocketError.Success)
        {
            ClientConsoleWrite("SocketError: {0}\r\n", socketError);
        }

        switch (e.LastOperation)
        {
            case SocketAsyncOperation.SendTo:
                {
                    if (socketError == SocketError.Success)
                    {
                        ClientConsoleWrite("Client message sent!\r\n");
                    }

                    ClientSendBuffer();

                    break;
                }
            case SocketAsyncOperation.ReceiveFrom:
                {
                    int bytesTransferred = e.BytesTransferred;

                    byte[] buffer = new byte[bytesTransferred];

                    Buffer.BlockCopy(e.Buffer, e.Offset, buffer, 0, bytesTransferred);

                    string message = Encoding.ASCII.GetString(buffer);

                    ClientConsoleWrite("Message received: {0}\r\n", message);

                    ClientReceive();

                    break;
                }
        }
    }

I figured this out. This issue is happening because the underlying buffer on the variable m_ClientSendArgs is constantly being changed using SetBuffer :

byte[] buffer = m_ClientSendQueue.Dequeue();

m_ClientSendArgs.SetBuffer(buffer, 0, buffer.Length);

When I assigned a static buffer to it and used Buffer.BlockCopy, the issue went away:

byte[] buffer = m_ClientSendQueue.Dequeue();

Buffer.BlockCopy(buffer, 0, m_ClientSendBuffer, 0, buffer.Length);

m_ClientSendArgs.SetBuffer(0, buffer.Length);

So I've been implementing it wrong all along. It's strange that it wasn't an issue on .NET 3.5, or an issue for TCP on .NET 4.0/4.5.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM