.NET:UDPClient.BeginReceive()无法运行的回调函数,如果报文收发靠得太近报文、回调、收发、太近

由网友(別钯洎紀抬啲冭髙)分享简介:让我说这是不相关的UDP作为协议的可靠性开始。我理解数据包可以被丢弃,到达的顺序等。此外,这不是数据速率的问题的严格地说的(我不是每秒处理太多的数据包),也不是它的数据量(不是一个问题,与客户端的基础缓冲器的大小)的问题。Let me start by saying this is not related to th...

让我说这是不相关的UDP作为协议的可靠性开始。我理解数据包可以被丢弃,到达的顺序等。此外,这不是数据速率的问题的严格地说的(我不是每秒处理太多的数据包),也不是它的数据量(不是一个问题,与客户端的基础缓冲器的大小)的问题。

Let me start by saying this is not related to the reliability of UDP as a protocol. I understand packets can be dropped, arrive out of order, etc. Also, this is not an issue of data rate strictly speaking (I am not trying to process way too many packets per second), nor is it an issue of the amount of data (not an issue with the size of the client's underlying buffer).

我的典型UDP监听看起来像这样(VB.NET如下):

My typical UDP listener looks like this (VB.NET follows):

Private ListenSocket As System.Net.Sockets.UdpClient
Private UDPSyncResult As System.IAsyncResult
Private UDPState As New Object
Private ReadOnly UDPReadLock As New Object
Private Sub StartListening()
    Try
        ListenSocket = New System.Net.Sockets.UdpClient
        ListenSocket.Client.SetSocketOption( _
            Net.Sockets.SocketOptionLevel.Socket, _
            Net.Sockets.SocketOptionName.ReuseAddress, True)
        ListenSocket.Client.Bind(New System.Net.IPEndPoint( _
            System.Net.IPAddress.Any, ListenPort))
        UDPSyncResult = ListenSocket.BeginReceive(AddressOf ProcessPacket, _
            UDPState)
    Catch ex As Exception
        'Handle an exception
    End Try
End Function

Private Sub ProcessPacket(ByVal ar As IAsyncResult)
    SyncLock UDPReadLock
        Try
            If ar Is UDPSyncResult Then
                Dim Data As Byte() = ListenSocket.EndReceive(ar, _
                    New System.Net.IPEndPoint(System.Net.IPAddress.Any, 0))
                ParsePacket(Data)
                UDPSyncResult = ListenSocket.BeginReceive( _
                    AddressOf ProcessPacket, UDPState)
            End If
        Catch ex As Exception
            Try
                UDPSyncResult = ListenSocket.BeginReceive( _
                    AddressOf ProcessPacket, UDPState)
            Catch ex2 As Exception
                'Do nothing
            End Try
        End Try
    End SyncLock
End Sub

ParsePacket 将是一个小组,做一些与数据。

ParsePacket would be a Sub that does something with the data.

如果你有一个服务器,它是,例如,发送两个数据包尽可能快地,每次用说,120个字节的数据,和一个第二(因此总数据速率是低的,但是每一对被发送一次,两个数据包到达完全相同的时间),那么什么情况是,很多时候可以看出, ProcessPacket 被调用两次,但不可避免的是,它会被调用一次,然后永远不再。也就是说,什么是杀害整个检测数据包,调用回调函数机制。

If you have a server which is, for example, sending two packets "as fast as possible", each with say, 120 bytes of data, and each pair is sent once a second (so total datarate is low, however, two packets arrive at "exactly the same time") then what happens is that often times it can be seen that ProcessPacket gets called twice, but inevitably, it will be called once and then never again. That is, something is killing the entire detect packet, call the callback function mechanism.

这非常看起来像线程冲突,因为它可以长时间工作,然后它会突然不工作相关的问题。是不是有什么毛病我code?

It very much looks like an issue related to thread collisions, because it can work for a long time and then it will suddenly not work at all. Is there something wrong with my code?

有关测试,这个问题可以很容易地通过运行一个简单的服务器复制:

For testing, the problem can easily be reproduced by running a simple server:

Private Sub StartTalking()
    Try
        TalkSocket = New System.Net.Sockets.UdpClient
        TalkSocket.Connect(System.Net.IPAddress.Parse(TalkIP), _
            TalkPort)
    Catch ex As Exception

    End Try
End Sub

Private Sub SendTwoPackets()
    Dim Packet As Byte() = AssemblePacket()
    Try
        TalkSocket.Send(Packet, Packet.Length)
        TalkSocket.Send(Packet, Packet.Length)
    Catch ex As Exception

    End Try
End Sub

指定 SendTwoPackets 来一个按钮,点击即可。 AssemblePacket 会是用于创建数据数组发送。

Assign SendTwoPackets to a button and click away. AssemblePacket would be something that creates the data array to send.

修改

重写我的回调,使有点整齐后,我意识到这个问题是我的支票 Ar是UDPSyncResult ;如果这是,然后我从来没有叫 BeginReceive 了。所以,现在我的回调更像是​​这样的:

After rewriting my callback to make it a bit neater, I realized the issue was with my check of ar Is UDPSyncResult; if this was False, then I'd never call BeginReceive again. So now my callback looks more like this:

Private Sub ProcessPacket(ByVal ar As IAsyncResult)
    SyncLock UDPReadLock
        Try
            Dim Data As Byte() = ListenSocket.EndReceive(ar, _
                New System.Net.IPEndPoint(System.Net.IPAddress.Any, 0))
            ParsePacket(Data)
            UDPSyncResult = ListenSocket.BeginReceive( _
                AddressOf ProcessPacket, UDPState)
        Catch ex As Exception
            Try
                UDPSyncResult = ListenSocket.BeginReceive( _
                    AddressOf ProcessPacket, UDPState)
            Catch ex2 As Exception
                'Do nothing
            End Try
        End Try
    End SyncLock
End Sub

我读了异步方法的 MSDN文章但老实说使用在这个例子回调是非常混乱给我。在我的code,那么我现在就忽略了的IAsyncResult 传递给回调,而且我从来不使用状态对象传递给 BeginReceive 。也许一个专家能告诉我写这个回调的最正确的方法是什么?

I read the MSDN article on asynchronous methods but honestly the use of a callback in that example is very confusing to me. In my code then I am now ignoring the IASyncResult passed to the callback, and I never use the "state object" passed to BeginReceive. Perhaps an expert can show me the "most correct" way of writing this callback?

推荐答案

有几件事我看到了蝙蝠的权利:

There are a couple of things I see right off the bat:

只有您的SyncLock 您code的最低限度的时间来获取数据。不处理它。

You should only SyncLock your code for the bare minimum time to get the data. Not process it.

它也像你正在尝试重新启动监听同时在尝试捕捉条款。此举对最后键,它会在两种情况下。

It also looks like you are trying to restart the listening in both the Try and Catch clauses. Move that to the Finally and it will work in both instances.

最后一件事我会做的是使 ParsePacket 运行在自己的线程每次调用的时间。然后,你会不会干涉数据的主线程上的接收。看看这篇文章在MSDN上:演练:编写一个简单的多线程组件使用Visual Basic

The last thing I would do would be to make ParsePacket run on its own thread each time it is called. Then you won't interfere with the receiving of data on the main thread. Take a look at this article on MSDN: Walkthrough: Authoring a Simple Multithreaded Component with Visual Basic

我会修改它是这样的:

Private Sub ProcessPacket(ByVal ar As IAsyncResult)
    Dim Data As Byte()

    SyncLock UDPReadLock
        Try
            Data = ListenSocket.EndReceive(ar, _
                New System.Net.IPEndPoint(System.Net.IPAddress.Any, 0))
        Catch ex As Exception
            ' Handle any exceptions here
        Finally
            Try
                UDPSyncResult = ListenSocket.BeginReceive( _
                    AddressOf ProcessPacket, UDPState)
            Catch ex As Exception
                ' Do nothing
            End Try
        End Try
    End SyncLock

    ' Only attempt to process if we received data
    If Data.Length > 0 Then
        ParsePacket(Data)
    End If
End Sub
阅读全文

相关推荐

最新文章