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

.net 为什么Socket.BeginReceive从UDP丢失数据包?,.net,vb.net,sockets,network-programming,udp,.net,Vb.net,Sockets,Network Programming,Udp,以下代码通过UDP等待数据。我有一个测试函数,可以发送1000个数据包(数据报?),每个数据包500字节。每次我运行测试函数时,接收器只会收到前几十个数据包,但会丢弃其余的数据包。我使用Wireshark查看了传入的网络数据,我看到所有1000个数据包都被实际接收到,但只是没有达到may应用程序的代码 下面是一些相关的VB.NET 3.5代码: Private _UdbBuffer As Byte() Private _ReceiveSocket As Socket Private _NumRe

以下代码通过UDP等待数据。我有一个测试函数,可以发送1000个数据包(数据报?),每个数据包500字节。每次我运行测试函数时,接收器只会收到前几十个数据包,但会丢弃其余的数据包。我使用Wireshark查看了传入的网络数据,我看到所有1000个数据包都被实际接收到,但只是没有达到may应用程序的代码

下面是一些相关的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
我发送的数据如下(暂时不担心数据包内容):

如果我在每次呼叫发送后加上一个小的延迟,更多的数据包就能通过;然而,既然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,我就有时间做我的事情(只要我不花太长时间检查可用的工作线程)

上面没有显示的是错误处理和处理UDP数据无序或丢失


我认为这可以解决我的问题,但是如果有人仍然认为上面的问题(或者我可以做得更好),我很想听听。UDP可以在任何时候丢弃数据包。在这种情况下,您可以尝试在接收器处设置一个更大的套接字接收缓冲区以减轻影响。

对不起。我不懂你的代码。为什么要在循环中包装异步方法?您应该从阅读异步处理开始

UDP只保证接收完整的消息,而不保证接收其他消息。消息可能会被丢弃或以错误的顺序出现。你需要应用你自己的算法来处理这个问题。例如,有一个叫做

其次,如果您希望在短时间内收到大量消息,则不应在收到新消息之前处理每条消息。相反,将每个传入消息排队,并有一个单独的线程负责处理


第三:Udp消息应使用BeginReceiveFrom/EndReceiveFrom进行异步处理,或使用ReceiveFrom进行同步处理。

如上所述,Udp不是可靠的协议。它是一种无连接协议,与TCP相比,IP数据包的开销要小得多。UDP对于许多功能(包括广播和多播消息)都很好,但不能用于可靠的消息传递。如果缓冲区过载,网络驱动程序将丢弃数据报。如果您需要可靠传递的基于消息的通信,或者您希望发送许多消息,您可以查看我们的产品(提供免费开源版本),该产品提供通过套接字以及UDP的基于消息的数据传输。

尝试更简单的方法。让接收器在单独的线程中运行,该线程在伪代码中类似如下:

do while socket_is_open???

  buffer as byte()

  socket receive buffer 'use the blocking version

  add buffer to queue of byte()

loop
在另一个线程循环中,根据队列大小确定是否已收到数据包。如果您已收到数据包,请对其进行处理,否则请休眠

do

if queue count > 0

  do 

    rcvdata = queue dequeue

    process the rcvdata

  loop while queue count > 0

else

  sleep

end if

loop while socket_is_open???

正如其他人已经说过的,UDP并不是一种有保证的交付机制。因此,即使wireshark向您显示数据包已发送,但这并不意味着数据包已在目的地接收。接收主机上的TCP/IP堆栈仍然可以丢弃数据报

您可以通过监视perfmon.exe中的以下性能计数器来确认这一情况

接收到本地计算机\IPv4\数据报 丢弃

接收到本地计算机\IPv6\数据报 丢弃

如果您使用的是IPv6协议


此外,您还可以尝试降低发送数据报的速率,看看这是否会降低丢弃率。

问题是,根据Wireshark的说法,所有发送到远程计算机的数据包都不会被UDP丢弃。而且,每次调用EndReceive都会得到500个字节,当我查看缓冲区的内容时,10000个字节中只有500个被填满,所以我认为它不会溢出。你指的是不同的缓冲区吗?UDP指的是不同的缓冲区
do while socket_is_open???

  buffer as byte()

  socket receive buffer 'use the blocking version

  add buffer to queue of byte()

loop
do

if queue count > 0

  do 

    rcvdata = queue dequeue

    process the rcvdata

  loop while queue count > 0

else

  sleep

end if

loop while socket_is_open???