【发布时间】:2011-04-17 03:00:38
【问题描述】:
以下代码通过 UDP 等待数据。我有一个测试功能,可以发送 1000 个数据包(数据报?),每个数据包 500 个字节。每次我运行测试函数时,接收者只得到前几十个数据包,但丢弃了其余的数据包。我使用 Wireshark 查看了传入的网络数据,我看到所有 1000 个数据包实际上都已收到,但只是不要让它成为可能的应用程序代码。
这里是一些相关的 VB.NET 3.5 代码:
Private _UdbBuffer As Byte()
Private _ReceiveSocket As Socket
Private _NumReceived As Integer = 0
Private _StopWaitHandle As AutoResetEvent
Private Sub UdpListen()
_StopWaitHandle = New AutoResetEvent(False)
_UdpEndPoint = New Net.IPEndPoint(Net.IPAddress.Any, UDP_PORT_NUM)
_ReceiveSocket = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
_ReceiveSocket.Bind(_UdpEndPoint)
ReDim _UdbBuffer(10000)
While Not _StopRequested
Dim ir As IAsyncResult = _ReceiveSocket.BeginReceive(_UdbBuffer, 0, 10000, SocketFlags.None, AddressOf UdpReceive, Nothing)
If Not _StopRequested Then
Dim waitHandles() As WaitHandle = {_StopWaitHandle, ir.AsyncWaitHandle}
If (WaitHandle.WaitAny(waitHandles) = 0) Then
Exit While
End If
End If
End While
_ReceiveSocket.Close()
End Sub
Private Sub UdpReceive(ByVal ar As IAsyncResult)
Dim len As Integer
If ar.IsCompleted Then
len = _ReceiveSocket.EndReceive(ar)
Threading.Interlocked.Increment(_NumReceived)
RaiseStatus("Got " & _NumReceived & " packets")
End If
End Sub
我发送数据如下(暂时不关心数据包内容):
For i as UShort = 0 to 999
Dim b(500) as Byte
_UdpClient.Send(b, b.Length)
Next
如果我在每次调用 Send 后添加一个小延迟,就会有更多的数据包通过;但是,由于 Wireshark 说无论如何它们都已收到,看来问题出在我的接收代码中。我应该提到 UdpListen 正在一个单独的线程上运行。
知道我为什么要丢包吗?我也试过 UdpClient.BeginReceive/EndReceive 但有同样的问题。
困扰我的第二个问题是使用套接字时接收缓冲区的全局性质,我不确定我是否没有足够快地处理传入的数据包,以至于缓冲区将被覆盖。目前还不确定该怎么做,但我愿意接受建议。
9 月 26 日:更新
根据对这篇文章和其他帖子的回复提出的各种相互矛盾的建议,我对我的代码进行了一些更改。感谢所有提出各种意见的人;我现在将所有数据包从拨号到快速以太网。正如你所看到的,这是我的代码有问题,而不是 UDP 丢弃数据包的事实(事实上,自从我修复以来,我没有看到超过一小部分数据包被丢弃或乱序)。
区别:
1) 将 BeginReceive()/EndReceive() 替换为 BeginReceiveFrom()/EndReceiveFrom()。不过,这本身并没有明显的影响。
2) 链接 BeginReceiveFrom() 调用,而不是等待异步句柄设置。不确定这里是否有任何好处。
3) 将 Socket.ReceiveBufferSize 显式设置为 500000,这足以以快速以太网速度传输 1 秒的数据。原来这是一个与传递给 BeginReceiveFrom() 的缓冲区不同的缓冲区。这有最大的好处。
4) 我还修改了我的发送例程,在发送一定数量的字节以根据预期带宽进行节流后等待几毫秒。这对我接收代码有很大的好处,即使 Wireshark 说即使没有这种延迟,我的所有数据仍然可以通过。
我最终没有使用单独的处理线程,因为据我了解,对 BeginReceiveFrom 的每次调用都会在新的工作线程上调用我的回调。这意味着我可以同时运行多个回调。这也意味着,一旦我调用 BeginReceiveFrom,我就有时间做我的事情(只要我不花太长时间并耗尽可用的工作线程)。
Private Sub StartUdpListen()
_UdpEndPoint = New Net.IPEndPoint(Net.IPAddress.Any, UDP_PORT_NUM)
_ReceiveSocket = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
_ReceiveSocket.ReceiveBufferSize = 500000
_ReceiveSocket.Bind(_UdpEndPoint)
ReDim _Buffer(50000)
_ReceiveSocket.BeginReceiveFrom(_Buffer, 0, _Buffer.Length, SocketFlags.None, _UdpEndPoint, AddressOf UdpReceive, Nothing)
End Sub
Private Sub UdpReceive(ByVal ar As IAsyncResult)
Dim len As Integer = _ReceiveSocket.EndReceiveFrom(ar, _UdpEndPoint)
Threading.Interlocked.Increment(udpreceived)
Dim receiveBytes As Byte()
ReDim receiveBytes(len - 1)
System.Buffer.BlockCopy(_Buffer, 0, receiveBytes, 0, len)
_ReceiveSocket.BeginReceiveFrom(_Buffer, 0, _UdbBuffer.Length, SocketFlags.None, _UdpEndPoint, AddressOf UdpReceive, Nothing)
//' At this point, do what we need to do with the data in the receiveBytes buffer
Trace.WriteLine("count=" & udpreceived)
End Sub
上面没有显示的是错误处理和处理 UDP 数据乱序或丢失。
我认为这可以解决我的问题,但如果有人仍然认为上述内容有任何问题(或者我可以做得更好),我很想听听。
【问题讨论】:
-
“困扰我的第二个问题是使用套接字时接收缓冲区的全局性质”是什么意思?每个插座有一个。没有什么全球性的。
-
“我不确定我是否没有足够快地处理传入的数据包,以至于缓冲区将被覆盖”。它不会被覆盖。如果缓冲区已满,传入的数据包将被丢弃。
-
@EJP,该评论适用于我的原始代码。考虑到我是如何使用缓冲区的,当我阅读它时,它可能会被下一组数据覆盖。新代码没有这个问题,因为它会在我回去监听更多数据之前对缓冲区(大约 200 个字节)进行块复制。
-
“RaiseStatus”方法非常繁重。您不想为收到的每个数据包调用它。
标签: .net vb.net sockets network-programming udp