.NET:UDPClient.BeginReceive()如果接收的数据包太近,则无法运行回调函数
让我首先说,这与UDP作为协议的可靠性无关。我理解数据包可能会被丢弃、无序到达等。此外,严格来说,这不是数据速率的问题(我不想每秒处理太多数据包),也不是数据量的问题(不是客户端底层缓冲区大小的问题) 我的典型UDP侦听器如下所示(VB.NET如下所示):.NET:UDPClient.BeginReceive()如果接收的数据包太近,则无法运行回调函数,.net,vb.net,multithreading,sockets,asynchronous,.net,Vb.net,Multithreading,Sockets,Asynchronous,让我首先说,这与UDP作为协议的可靠性无关。我理解数据包可能会被丢弃、无序到达等。此外,严格来说,这不是数据速率的问题(我不想每秒处理太多数据包),也不是数据量的问题(不是客户端底层缓冲区大小的问题) 我的典型UDP侦听器如下所示(VB.NET如下所示): ParsePacket将是处理数据的子包 例如,如果您有一台服务器,它“尽可能快地”发送两个数据包,每个数据包都有120字节的数据,并且每对数据每秒发送一次(因此总数据率很低,但是,两个数据包到达“完全相同的时间”),那么通常可以看到Proc
ParsePacket
将是处理数据的子包
例如,如果您有一台服务器,它“尽可能快地”发送两个数据包,每个数据包都有120字节的数据,并且每对数据每秒发送一次(因此总数据率很低,但是,两个数据包到达“完全相同的时间”),那么通常可以看到ProcessPacket
被调用两次,但不可避免的是,它将被调用一次,然后再也不会调用。也就是说,有东西正在杀死整个检测包,调用回调函数机制
这看起来很像是一个与线程冲突有关的问题,因为它可以工作很长一段时间,然后突然完全不工作。我的代码有问题吗
为了进行测试,可以通过运行简单的服务器很容易地重现问题:
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
分配给一个按钮,然后单击“离开”assembleepacket
将创建要发送的数据数组
编辑
在重写回调以使其更整洁之后,我意识到问题在于检查了ar Is UDPSyncResult
;如果这是False
,那么我再也不会调用BeginReceive
。现在我的回调看起来更像这样:
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
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
我读过,但老实说,在那个例子中使用回调对我来说非常混乱。在我的代码中,我现在忽略传递给回调的
IASyncResult
,并且我从不使用传递给BeginReceive
的“state对象”。也许专家可以告诉我写这个回调的“最正确”的方法?我马上就看到了一些东西:
您应该只在获取数据的最短时间内SyncLock
您的代码。不要处理它
看起来您正在尝试重新启动Try
和Catch
子句中的侦听。将其移动到最后
,它将在两种情况下都起作用
我要做的最后一件事是让ParsePacket
在每次调用它时在它自己的线程上运行。这样就不会干扰主线程上的数据接收。看看这篇关于MSDN的文章:
我会将其修改为如下内容:
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
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
谢谢你的评论。我认为将
ParsePacket
放在它自己的线程上是多余的,因为回调已经在它自己的线程上被调用了,不是吗?我不这么认为。另外,您希望ProcessPacket
尽快响应;每次回调运行时,它都在Visual Studio的线程窗口描述的“工作线程”上运行,System.Threading.Thread.CurrentThread.ManagedThreadId
每次都不同。这意味着每次回调都在它自己的线程上。因此,我不认为使用另一个thread.SyncLock有什么意义。SyncLock可以用于线程,但它的目的是防止多个进程或线程同时访问单个资源。在您的情况下,您不希望任何其他进程在访问套接字时能够使用它ProcessPacket
将始终位于工作线程上,因为调用ListenSocket.BeginReceive
会启动另一个线程<默认情况下,代码>解析包将出现在主线程上。为了防止竞争条件,我将在一个单独的线程上执行ParsePacket
。这将允许您处理太多数据包同时到达的情况。