使用SocketAsyncEventArgs进行UDP异步发送,SAEA复用的情况下,会出现一些不能理解的问题,具体描述在代码中三个发送函数总有注释,望大神能指点一二,感激不尽。
异步UDP代码
Imports System.Net
Imports System.Net.Sockets
Public Class UDP
Private m_SaeaReceive As New SocketAsyncEventArgs
Private m_SaeaSend1 As New SocketAsyncEventArgs
Private m_SaeaSend2 As New SocketAsyncEventArgs
Private m_BufferSend(65506) As Byte
Private m_BufferReceive(65506) As Byte
Private m_Udp As Socket
Public Sub Start(ByVal Port As UInt16)
m_Udp = New Socket(IPAddress.Any.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
m_Udp.Bind(New IPEndPoint(IPAddress.Any, Port))
m_SaeaReceive.SetBuffer(m_BufferReceive, 0, m_BufferReceive.Length)
m_SaeaReceive.RemoteEndPoint = New IPEndPoint(IPAddress.Any, 0)
m_SaeaSend2.SetBuffer(m_BufferSend, 0, m_BufferSend.Length)
AddHandler m_SaeaReceive.Completed, AddressOf Saea_Completed
AddHandler m_SaeaSend1.Completed, AddressOf Saea_Completed
AddHandler m_SaeaSend2.Completed, AddressOf Saea_Completed
StartReceive()
End Sub
Public Sub SendTo1(ByVal Remote As IPEndPoint, ByVal Data() As Byte)
If Remote Is Nothing Then Return
If Data Is Nothing Then Return
If Data.Length = 0 Then Return
'重复使用一个Saea,会在使用一定次数后出现SocetError=AddressFamilyNotSupported的情况
'出现次数相对固定,本机测试一次发送10240字节的数据,第一次差不多在210次左右出现。
'与此同时SendAsync第一次进入了同步发送完毕,即SendAsync()返回False。
'这个Saea不能再使用,如果尝试用此Saea重发,仍会出现上述情况。
'在发送异步回调处理中使用故障Saea的Remote和Buffer重新创建一个Saea,再重新发送,可以发送成功。
'上述问题一般会出现两次,且两次的次数都相对固定,很难再现第三次。
Try
m_SaeaSend1.RemoteEndPoint = Remote
m_SaeaSend1.SetBuffer(Data, 0, Data.Length)
If m_Udp.SendToAsync(m_SaeaSend1) = False Then
'同步完成,上述问题均出现在同步完成的情况下。
SendedToHandler(m_SaeaSend1)
End If
Catch ex As Exception
Return
End Try
End Sub
Public Sub SendTo2(ByVal Remote As IPEndPoint, ByVal Data() As Byte)
If Remote Is Nothing Then Return
If Data Is Nothing Then Return
If Data.Length = 0 Then Return
Try
'给Saea预先分配一个缓冲,之后每次将数据复制到此缓冲中,不会出现发送问题
m_SaeaSend2.RemoteEndPoint = Remote
Array.Copy(Data, 0, m_SaeaSend2.Buffer, 0, Data.Length)
m_SaeaSend2.SetBuffer(0, Data.Length)
If m_Udp.SendToAsync(m_SaeaSend2) = False Then
SendedToHandler(m_SaeaSend2)
End If
Catch ex As Exception
Return
End Try
End Sub
Public Sub SendTo3(ByVal Remote As IPEndPoint, ByVal Data() As Byte)
If Remote Is Nothing Then Return
If Data Is Nothing Then Return
If Data.Length = 0 Then Return
Try
'每次都新建一个Saea来发送,不会出现问题
Dim saea As New SocketAsyncEventArgs
AddHandler saea.Completed, AddressOf Saea_Completed
saea.RemoteEndPoint = Remote
saea.SetBuffer(Data, 0, Data.Length)
If m_Udp.SendToAsync(saea) = False Then
SendedToHandler(saea)
End If
Catch ex As Exception
Return
End Try
End Sub
Private Sub StartReceive()
If m_Udp.ReceiveFromAsync(m_SaeaReceive) = False Then
ReceivedFromHandler(m_SaeaReceive)
End If
End Sub
'异步完成事件处理,分流
Private Sub Saea_Completed(ByVal sender As Object, ByVal e As SocketAsyncEventArgs)
Select Case e.LastOperation
Case SocketAsyncOperation.SendTo
SendedToHandler(e)
Case SocketAsyncOperation.ReceiveFrom
ReceivedFromHandler(e)
End Select
End Sub
Private Sub SendedToHandler(ByVal e As SocketAsyncEventArgs)
Static index As Integer = 0
index = index + 1
If e.SocketError = SocketError.Success Then
Debug.Print(index.ToString.PadLeft(5, "0") & " sended success:" & e.BytesTransferred & " bytes")
ElseIf e.SocketError = SocketError.AddressFamilyNotSupported Then
Debug.Print(index.ToString.PadLeft(5, "0") & " send failed:" & e.SocketError.ToString & " ================================================================")
'重建一个Saea重新发送可以成功
'm_SaeaSend1 = New SocketAsyncEventArgs
'm_SaeaSend1.RemoteEndPoint = e.RemoteEndPoint
'm_SaeaSend1.SetBuffer(e.Buffer, e.Offset, e.Count)
'AddHandler m_SaeaSend1.Completed, AddressOf Saea_Completed
'If m_Udp.SendAsync(m_SaeaSend1) = False Then
' SendedToHandler(m_SaeaSend1)
'End If
Else
Debug.Print(index.ToString.PadLeft(5, "0") & " send failed:" & e.SocketError.ToString)
End If
End Sub
Private Sub ReceivedFromHandler(ByVal e As SocketAsyncEventArgs)
Static index As Integer = 0
index = index + 1
If e.SocketError = SocketError.Success Then
Debug.Print(" " & index.ToString.PadLeft(5, "0") & " received success:" & e.BytesTransferred & " bytes")
StartReceive()
Else
Debug.Print(" " & index.ToString.PadLeft(5, "0") & " receive failed:" & e.SocketError.ToString)
End If
End Sub
End Class
测试代码
Imports System.Net
Imports System.Threading
Public Class Form2
Private server As New UDP
Private client As New UDP
Private buff_comm(10240 - 1) As Byte
Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
server.Start(6000)
client.Start(7000)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Do
Dim buff(1024 * 10 - 1) As Byte
client.SendTo1(New IPEndPoint(IPAddress.Parse("192.168.1.3"), 6000), buff)
Thread.Sleep(50)
Loop
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Do
Dim buff(1024 * 10 - 1) As Byte
client.SendTo2(New IPEndPoint(IPAddress.Parse("192.168.1.3"), 6000), buff)
Thread.Sleep(50)
Loop
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Do
Dim buff(1024 * 10 - 1) As Byte
client.SendTo3(New IPEndPoint(IPAddress.Parse("192.168.1.3"), 6000), buff)
Thread.Sleep(50)
Loop
End Sub
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
Do
client.SendTo1(New IPEndPoint(IPAddress.Parse("192.168.1.3"), 6000), buff_comm)
Thread.Sleep(50)
Loop
End Sub
End Class