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

4

2 回答 2

2

我不是在回答这个问题,但我确实需要指出一些非常重要的事情:

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

不要隐藏这样的异常。当某事失败时,您将没有机会尝试修复它,因为您永远不会知道为什么某事失败了。请使用适当的异常处理。

由于应用程序,我需要 UDP 套接字的快速、非阻塞操作

这种说法是不正确的。非阻塞套接字并不快,它们只是在操作完成之前返回。

我确实建议您切换回阻塞套接字,因为您似乎是套接字编程的新手。先让应用程序运行,然后再尝试优化它。

于 2011-04-21T11:50:40.183 回答
0

您正在将要从中读取消息的套接字设置为非阻塞。如果操作不能立即完成,这将指示套接字不阻塞。这实际上意味着如果您尝试从套接字读取并且没有等待读取的内容,则调用将不会成功返回。

我不知道是如何MessageReceived被调用的,但是我会假设无论调用它是不是在调用之前检查信息是否实际上已经准备好从套接字读取。

当您遇到间歇性问题时,这表明大多数时候,当MessageReceived被调用时,有数据要从套接字读取。

如果您想继续使用非阻塞 IO,您需要更改您的逻辑,以便它捕获 IO 异常并在短暂延迟后重试(如果您确定那里会有数据),或者检查在尝试执行读取之前,查看是否有实际可从套接字读取的数据。

检查套接字上是否有可用信息(在尝试从中读取之前)的一种方法是使用 Socket.Poll。就像是:

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

您可能还需要检查SelectError状态,以确定套接字是否发生故障。我的大部分套接字编程都来自 C++,所以我不确定 .Net 的细节。

于 2011-04-21T11:37:49.350 回答