2

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

Serge
  • 63
  • 1
  • 1
  • 8
  • 1
    Setting the Blocking flag to false, tells the socket that if the data isn't there, don't block, in which case you get the error you're receiving. If the Blocking flag was true, the call would simply block until it had received the information and then return from the call. You need to do something else, then try again a bit later. – forsvarir Apr 21 '11 at 11:20
  • How is MessageReceived triggered/called? – forsvarir Apr 21 '11 at 11:25
  • I modified the original post to include how MessageReceived is called. Baiscally it is within a while loop which is a timeout. – Serge Apr 21 '11 at 11:36

2 Answers2

2

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.

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • 1
    @Serge: Sadly, I tend to agree with jgauffin. Non-blocking sockets, whilst they tend to be more fun and can allow you to manage more concurrent connections with fewer threads, aren't faster. They're also more complex to manage, so shouldn't really be used unless you have a reason for it. Looking at your while loop, it's effectively blocking on the socket anyway so you're getting no obvious benefit from using non-blocking sockets. – forsvarir Apr 21 '11 at 11:57
  • Guess that @Serge commented and removed the comment? Non-blocking sockets are not faster. Feel free to do benchmarks. What **will** slow down performance is using one thread per client. But not directly, you need to get a *bunch* of simultaneous clients before that happens (and when that happens I do recommend that you switch to asynchronous socket methods and not non-blocking sockets). But do not do anything more complex until you really need to do so. – jgauffin Apr 21 '11 at 12:04
  • Without going in to too much details there is only one client per server. The Linux machine is a hard real time system that receives commands from Windows UI machine and executes appropriate tasks. I agree with what you said about non blocking sockets, but as I said I dont code for Windows much and not very familiar with .NET hence I rewritten my C++ socket implementation for real time environment. Hence I can not sit and infinitely wait with a blocking socket I have to allocate time and just skip it if its failed. Hence non blocking gives me more freedom. The problem described is with .NET – Serge Apr 21 '11 at 12:19
  • Then switch to asynchronous methods in the linux application instead of non-blocking sockets and stick to blocking sockets in the windows application. It forces you to code in a certain way and the linux app will still be responsive since the async methods (`BeginReceive`/`EndReceive`) do not block. – jgauffin Apr 21 '11 at 12:27
  • Having Blocking set to true in Windows simply hangs the application and never comes out of it. I know for sure the datagrams arrive as I can see it on Wireshark but the receive call never returns and just hangs the application. I need strict control of timing with Real Time application. I'm not very sure how async will help me with this. I dont have the problem between RT linux machines at all no matter how heavy the traffic is, the problem is with .NET and how receive call is handled, which I am totally unfamiliar with and is causing issues for UI. – Serge Apr 21 '11 at 12:44
  • As I understand it you are using .NET in both applications? If so, my suggestion will make it easier for you. If not, just change the windows app. Have you looked at the example in MSDN: http://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.aspx ? – jgauffin Apr 21 '11 at 12:50
  • The system that I have is a distributed controller. A low level controller running RT patched Linux which communicates with a high level controller running RT patched Linux which communicates with Windows UI machine. The user can monitor both RT systems and issue commands to high level controller which then processes it and talks to a low level controller. I will see if I can rewrite .NET code using the link you sent me. I was just hoping for an easy fix... Guess not this time :) – Serge Apr 21 '11 at 13:00
  • As I said before I took my C++ code written for RT needs and tried to directly port it to .NET which by the looks of things hates me... :) – Serge Apr 21 '11 at 13:01
0

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.

forsvarir
  • 10,749
  • 6
  • 46
  • 77
  • In my while loop as long as timer hasn't yet expired I keep checking again and again. Once message successfully received I instruct the code to clear the buffer and be ready to do the next receive. – Serge Apr 21 '11 at 11:49
  • The UDP preserves message boundaries and does segmentation and reassembly automatically before it is available for reading as far as I understand it. Also the UDP datagrams that fly around the network is set to 32 bytes in my case. In C++ I use peek to check for data present to read, I guess Poll does similar thing here. Thanks a lot! – Serge Apr 21 '11 at 12:25