.net - 为什么 Socket.BeginReceive 会丢失 UDP 数据包?

标签 .net vb.net sockets network-programming udp

以下代码等待通过 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数据乱序或丢失的处理。

我认为这可以解决我的问题,但如果有人仍然认为上述内容有任何问题(或者我可以做得更好),我很想听听。

最佳答案

UDP 可以随时丢弃数据包。在这种情况下,您可以尝试在接收器处设置更大的套接字接收缓冲区来缓解。

关于.net - 为什么 Socket.BeginReceive 会丢失 UDP 数据包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3793104/

相关文章:

.net - 为什么too​​lstriplabel 的backcolor 属性在设计时或运行时不会改变?

.net - .NET 中文本文件资源的本地化

MySqlConnection' 在命名空间'MySql.Data.MySqlClient [Visual Basic -VS2008] 中不明确

c 获取服务器公共(public)IP

python - IRC 机器人无法连接到服务器 - Python

c# - 如何强行关闭 TcpListener

c# - C#中两个进程之间的同步。?

c++ - 添加对 VS 6.0 C++ 帮助的引用!

.net - 比较两个列表以获取同时出现在两个列表中的对象

.net - 当 ShowinTaskBar 为 false 时,窗口消失