让我说这是不相关的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
相关推荐
最新文章