开发者

Non blocking UDP socket .NET "A non-blocking socket operation could not be completed immediately" exception

I have written a small UDP client server class in C# that is used to provide comms between a Linux and a Windows machines.

The implementation of the UDP client and server in C# in Windows is a direct rewrite from C++ code I wrote for Linux originally.

I have no problems during run time between Linux machines but there is an intermittent problem that occasionally appears between Linux and Windows link.

Due to the application I need fast, non blocking operation of the UDP socket.

Since one client is Linux the code under C# I had to use some magic of marshalling.

Here is the code:

    public bool Connect(string sIPAddr, int portNumber)
    {
        try
        {
            if (portNumber > 65535 && portNumber < 0)
            {
                this._isReady = false;
                return this._isReady;
            }

            this._ipPort = portNumber;
            this._ipAddress = IPAddress.Parse(sIPAddr);
            IPEndPoint ipep = new IPEndPoint(this._ipAddress, this._ipPort);
            this._myUDPClient = new Socket(ipep.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
         开发者_JAVA百科   this._myUDPClient.Blocking = false;
            this._myUDPClient.Connect(this._ipAddress, this._ipPort);

            this._isReady = true;
            return this._isReady;
        }
        catch (Exception)
        {
            this._isReady = false;
            return this._isReady;
        }
    }

I use connect on UDP to simplify send and receive calls.

The problem happens when I try and read from the socket.

More code:

    public bool NewMessageReceived()
    {
        try
        {
            if (this._newMessaageReceived)
            {
                return this._newMessaageReceived;
            }
            else
            {
                _messageBuffer = new byte[65507];
                int numBytesRcvd = _myUDPClient.Receive(this._messageBuffer, 65507, SocketFlags.None);
                Marshal.Copy(_messageBuffer, 0, _pmessageBuffer, 65507);

                if (numBytesRcvd < 0)
                {
                    this._newMessaageReceived = false;

                    // TODO: Add Socket Error Checking
                }
                else
                {
                    this._newMessaageReceived = true;
                }

                Array.Clear(_messageBuffer, 0, _messageBuffer.GetLength(0));
                return this._newMessaageReceived;
            }
        }
        catch (Exception e)
        {
            System.Windows.Forms.MessageBox.Show(e.Message);
            return false;
        }
    }

I have Wireshark running on both machines and I can see that the datagram sent from Linux machine arrives on Windows machine unharmed. However the UDP client Receive call throws and exception saying: "A non-blocking socket operation could not be completed immediately" which from what I understand is a WSAEWOULDBLOCK error. However I explicitly set blocking option to false.

The sequence of events is the following:

Windows machine sends a datagram on port 2 and listens for acknowledge on port 1. I have a while loop which implements timeout

Code:

        DateTime TimeAtStart = new DateTime();
        TimeAtStart = DateTime.Now;
        TimeSpan TimeOut = new TimeSpan(0,0,0,0,800);

        IntPtr RecievedTelPtr = new IntPtr();
        bool UnpackingResult;

        while (TimeOut > (DateTime.Now - TimeAtStart))
        {
            if (!NackAckRecieveConnection.GetIsReady())
            {
                ErrorEventArguements.SetAllHmiNetworkEventArgs(ID, -3, 2);
                return (false);
            }
            if (NackAckRecieveConnection.NewMessageReceived())
            {
                RecievedTelPtr = NackAckRecieveConnection.GetMessage();
                UnpackingResult = UnpackHmiTelegram(RecievedTelPtr, AckNackType);
                NackAckRecieveConnection.MessageRetrieved();
                return (UnpackingResult);
            }
        }
        //if escape loop return timeout err msg
        ErrorEventArguements.SetAllHmiNetworkEventArgs(ID, -4, (AckNackType == 0) ? (1) : (3));
        return (false);

I would like to be able to understand the issue and why the problem occurs and how can I fix it as I have fun out of ideas.

Thank you


I'm not answering the question, but I do need to point out something very important:

    catch (Exception)
    {
        this._isReady = false;
        return this._isReady;
    }

Do NOT hide exceptions like that. When something fails you will have no chance what so ever to try to fix it, since you will never know why something failed. Do use proper exception handling.

Due to the application I need fast, non blocking operation of the UDP socket

That statement is not correct. Non-blocking sockets are not faster, they simply return before the operation has completed.

I do recommend that you switch back to blocking sockets, since you seem to be new to socket programming. Get the application running first, then try to optimize it.


You are setting the socket that you're reading messages from to non-blocking. This instructs the socket to NOT BLOCK if the operation cannot be completed immediately. What this means in practical terms is that if you attempt to read from the socket and there is nothing waiting to be read, the call will not return successfully.

I don't know how MessageReceived is being called, however I would assume that whatever is calling it is not checking that information is actually ready to be read from the socket, prior to the call.

As you're experiencing an intermittent problem, it would suggest that most of the time, the when MessageReceived is being called, there is data to be read from the socket.

If you want to continue to use non-blocking IO, you need to either change your logic, so that it catches the IO exception and retrys after a short delay (if you're sure there's going to be data there), or check to see if there is actually data available to be read from the socket, prior to attempting to perform the read.

One way to check if there is information available on the socket (prior to attempting to read from it) would be to use Socket.Poll. Something like:

if (_myUDPClient.Poll(myTimeoutInMicroSeconds, SelectMode.SelectRead)){
       // Try to read the new messsage
} else {
    continue;  // go back to top of loop and try again
}

You may also need to check for SelectError state, to determine if the socket has a failure. Most of my socket programming has been from C++, so I'm not sure about the .Net specifics.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜