gpt4 book ai didi

.NET:如果接收到的数据包靠得太近,UDPClient.BeginReceive() 将无法运行回调函数

转载 作者:行者123 更新时间:2023-12-03 11:54:15 24 4
gpt4 key购买 nike

首先让我说这与 UDP 作为协议(protocol)的可靠性无关。我知道数据包可能会被丢弃、乱序到达等。另外,严格来说,这不是数据速率的问题(我不是试图每秒处理太多的数据包),也不是数据量的问题数据(不是客户端底层缓冲区大小的问题)。

我的典型 UDP 监听器如下所示(VB.NET 如下):

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将是一个对数据执行某些操作的 Sub。

例如,如果您的服务器“尽可能快”地发送两个数据包,每个数据包都包含 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到一个按钮,然后点击离开。 AssemblePacket将是创建要发送的数据数组的东西。

编辑

在重写我的回调使其更整洁之后,我意识到问题出在我对 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

我读了 MSDN article on asynchronous methods但老实说,在那个例子中使用回调让我很困惑。在我的代码中,我现在忽略了 IASyncResult传递给回调,我从不使用传递给 BeginReceive 的“状态对象” .也许专家可以告诉我写这个回调的“最正确”的方式?

最佳答案

我立即看到了几件事:

你应该只 SyncLock您的代码在最短的时间内获取数据。不处理。

看起来您还试图在 Try 中重新开始监听。和 Catch条款。将其移至 Finally它在这两种情况下都有效。

我要做的最后一件事是制作 ParsePacket每次调用时都在自己的线程上运行。这样你就不会干扰主线程上的数据接收了。看看 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

关于.NET:如果接收到的数据包靠得太近,UDPClient.BeginReceive() 将无法运行回调函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21366887/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com