非阻塞 UDP 套接字 .NET“无法立即完成非阻塞套接字操作”例外

发布于 2024-11-03 03:10:25 字数 3664 浏览 3 评论 0原文

我用 C# 编写了一个小型 UDP 客户端服务器类,用于在 Linux 和 Windows 机器之间提供通信。

Windows 中 C# 的 UDP 客户端和服务器的实现是我最初为 Linux 编写的 C++ 代码的直接重写。

我在 Linux 机器之间运行时没有出现任何问题,但在 Linux 和 Windows 链接之间偶尔会出现间歇性问题。

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

由于一个客户端是 Linux,C# 下的代码我必须使用一些编组魔法。

这是代码:

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

我使用 UDP 上的连接来简化发送和接收呼叫。

当我尝试从套接字读取数据时,就会出现问题。

更多代码:

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

我在两台机器上运行 Wireshark,我可以看到从 Linux 机器发送的数据报完好无损地到达 Windows 机器。但是 UDP 客户端 Receive 调用会抛出异常并显示:“无法完成非阻塞套接字操作 立即”,据我了解,这是一个 WSAEWOULDBLOCK 错误。但是,我明确地将阻止选项设置为 false。

事件顺序如下:

Windows 计算机在端口 2 上发送数据报并在端口 1 上侦听确认。有一个实现超时的 while 循环

代码:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夜夜流光相皎洁 2024-11-10 03:10:25

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

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

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

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

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

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

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.

怀里藏娇 2024-11-10 03:10:25

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

我不知道如何调用 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 的细节。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文