以下是相关的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,但问题依然存在。
另一个困扰我的问题是使用Sockets时接收缓冲区的全局性质,如果我没有快速处理传入的数据包,缓冲区会被覆盖。目前还不知道该怎么做,但我愿意听取建议。
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数据乱序或丢失的内容。
我认为这样可以解决我的问题,但如果有人仍然发现上述内容有任何问题(或者我可以做得更好),我很愿意听取意见。