UDP SocketException - 通常只允许使用每个套接字地址

时间:2013-07-22 08:28:08

标签: vb.net sockets udp

虽然这里有很多非常相似的问题,所提供的答案都没有帮助我,这让我感到难过:(

我有一个非常大的管理系统,我一直负责编写一些UDP数据包发送/接收。我已经写了一个原型,一切都很好,所以我开始将我的代码合并到所述系统中。但是,我现在有一个(不显示停止但很恼人)SocketException弹出:

System.Net.Sockets.SocketException occurred
  ErrorCode=10048
  Message=Only one usage of each socket address (protocol/network address/port) is normally permitted
  NativeErrorCode=10048
  Source=System
  StackTrace:
       at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
       at System.Net.Sockets.Socket.Bind(EndPoint localEP)
       at System.Net.Sockets.UdpClient..ctor(Int32 port, AddressFamily family)
       at System.Net.Sockets.UdpClient..ctor(Int32 port)
       at Goose.Job.DeviceServerUDPReceiver.InitialiseReceiverClient() in C:\WORK\Trunk\GooseOrders\Classes\SheetCounter\DeviceServerUDPReceiver.vb:line 39

这是UDPReceiver类 - 它负责只是坐在一个循环中,等待来自我们已经点缀过这个地方的设备服务器的响应。

Public Class DeviceServerUDPReceiver : Implements IDisposable
'///////////////////////////////////////////////////////////////////////////////
' CONSTANTS
'///////////////////////////////////////////////////////////////////////////////
Private Const TIBBO_DEVICE_REPLY_CMD_START As Integer = 0
Private Const TIBBO_DEVICE_REPLY_CMD_END As Integer = 3
Private Const TIBBO_MESSAGE_REPLY_DIVIDER As String = "_"
Private Const TIBBO_DEVICE_REPLY_OK As String = "OK"

'///////////////////////////////////////////////////////////////////////////////
' MEMBER VARIABLES
'///////////////////////////////////////////////////////////////////////////////
Public _ReceivingClient As System.Net.Sockets.UdpClient
Public _iReceivingPort As Integer = 2002
Public _thReceivingThread As System.Threading.Thread
Public _bClosing As Boolean

'///////////////////////////////////////////////////////////////////////////////
' EVENTS
'///////////////////////////////////////////////////////////////////////////////
Public Event GotDeviceResponse(ByVal sResponse As String)
Public Event FoundNewDevice(ByVal TibboObject As TibboDevice)

'///////////////////////////////////////////////////////////////////////////////
' METHODS
'///////////////////////////////////////////////////////////////////////////////
' Initialises the UDP receiver client on the specified port number. Then runs
' a listening thread constantly waiting to receive udp messages
Public Sub InitialiseReceiverClient()
    Try
        ' TODO - FIX SOCKET EXCEPTION HERE - NOT THREAD ISSUE - THIS IS DUE TO 
        ' THE SOCKET NOT BEING CLOSED. BUT SEEING HOW UDP IS CONNECTIONLESS .... ?!
        _ReceivingClient = New System.Net.Sockets.UdpClient(_iReceivingPort)
        Dim thStartThread As Threading.ThreadStart = New Threading.ThreadStart(AddressOf SitAndReceive)
        _thReceivingThread = New Threading.Thread(thStartThread)
        _thReceivingThread.IsBackground = True
        _thReceivingThread.Start()
    Catch ex As System.Net.Sockets.SocketException
        Console.WriteLine("Socket Exception: " & ex.Message)
    Finally

    End Try
End Sub

' The endless loop listener thread. Will sit and wait for udp packets to 
' process
Private Sub SitAndReceive()
    Dim epEndPoint As System.Net.IPEndPoint = New System.Net.IPEndPoint(System.Net.IPAddress.Any, _iReceivingPort)

    ' infinite loop to listen for udp messages
    While (_bClosing = False)
        Try
            Dim sMessage As String = ""
            Dim byData() As Byte

            byData = _ReceivingClient.Receive(epEndPoint)
            sMessage = System.Text.Encoding.ASCII.GetString(byData)
            Console.WriteLine(sMessage)

            ProcessIncomingUDPDataMessage(sMessage)

        Catch ex As System.Net.Sockets.SocketException
            Console.WriteLine(ex.Message)
        End Try
    End While
End Sub

' close the connection to the receiving udp socket
Public Sub Close()
    _bClosing = True
End Sub


' Processes incoming udp packets for answeres from the device servers
Private Sub ProcessIncomingUDPDataMessage(ByVal sMessage As String)

    ' UDP Data packet from Tibbo devices is set out as follows
    '
    ' CMD_ANSWER
    ' Where "CMD" = The command the device is replying too and
    ' "ANSWER" = It's reply
    Select Case sMessage.Substring(TIBBO_DEVICE_REPLY_CMD_START, TIBBO_DEVICE_REPLY_CMD_END)
        Case TibboDevice.DEVICE_COMMAND_ATO
            '/////////////////////////////////////////////////////////////////////////
            ' Any Tibbo's out there reply message 
            '/////////////////////////////////////////////////////////////////////////
            Dim s() As String = sMessage.Split(TIBBO_MESSAGE_REPLY_DIVIDER)
            Dim sMacAddress As String = s(2) ' the replying devices' mac address
            Dim sIpAddress As System.Net.IPAddress = System.Net.IPAddress.Parse(s(3)) ' ip
            Dim sNetBiosName As String = s(1) ' netbios name
            Dim iTibboStatus As TibboDevice.ETIIBO_DEVICE_STATE = TibboDevice.ETIIBO_DEVICE_STATE.TIBBO_DEVICE_STATE_BAD ' status

            ' set this device status depending on the reply
            If s(4) = TIBBO_DEVICE_REPLY_OK Then
                iTibboStatus = TibboDevice.ETIIBO_DEVICE_STATE.TIBBO_DEVICE_STATE_OK
            End If

            ' create a new tibbo device to pass back to the main form
            Dim Tibbo As TibboDevice = New TibboDevice(sMacAddress, sIpAddress, sNetBiosName, iTibboStatus)
            ' raise event to add this to our list
            RaiseEvent FoundNewDevice(Tibbo)


        Case TibboDevice.DEVICE_COMMAND_STS
            '//////////////////////////////////////////////////////////////////////////
            ' Status reply message
            '//////////////////////////////////////////////////////////////////////////
            Dim s() As String = sMessage.Split(TIBBO_MESSAGE_REPLY_DIVIDER)
            Dim sResult As String = ""

            ' format our string nicely
            sResult &= "Mac Address: " & vbTab & vbTab & s(1)
            sResult &= Environment.NewLine & "IP Address: " & vbTab & vbTab & s(2)
            sResult &= Environment.NewLine & "Device Name: " & vbTab & vbTab & s(3)
            sResult &= Environment.NewLine & "TiOS FW: " & vbTab & vbTab & s(4)
            sResult &= Environment.NewLine & "Goose SC FW: " & vbTab & vbTab & s(5)
            sResult &= Environment.NewLine & "System Uptime: " & vbTab & vbTab & s(6)
            sResult &= Environment.NewLine & "System Time: " & vbTab & vbTab & s(7)
            sResult &= Environment.NewLine & "System Status: " & vbTab & vbTab & s(8)

            RaiseEvent GotDeviceResponse(sResult)

        Case TibboDevice.DEVICE_COMMAND_ASC
            '////////////////////////////////////////////////////////////////////////////
            ' Average sheet count message
            '////////////////////////////////////////////////////////////////////////////
            Dim s() As String = sMessage.Split(TIBBO_MESSAGE_REPLY_DIVIDER)
            Dim sResult As String = ""

            RaiseEvent GotDeviceResponse(sResult)

        Case TibboDevice.DEVICE_COMMAND_NAM
            '////////////////////////////////////////////////////////////////////////////
            ' Changed device name reply message
            ' Device will reply NAM_[NEWNAME] - once it's set it's new name
            '////////////////////////////////////////////////////////////////////////////
            Dim s() As String = sMessage.Split(TIBBO_MESSAGE_REPLY_DIVIDER)
            Dim sResult As String = ""

            RaiseEvent GotDeviceResponse(sResult)

        Case TibboDevice.DEVICE_COMMAND_IDX
            '////////////////////////////////////////////////////////////////////////////
            ' Device responds with it's device id
            '////////////////////////////////////////////////////////////////////////////
            Dim s() As String = sMessage.Split(TIBBO_MESSAGE_REPLY_DIVIDER)
            Dim sResult As String = ""

            ' TODO - do something with the result

        Case TibboDevice.DEVICE_COMMAND_RBT
            '////////////////////////////////////////////////////////////////////////////
            ' Device is going down for a reboot - not much to do here, we have to wait
            '////////////////////////////////////////////////////////////////////////////

        Case TibboDevice.DEVICE_COMMAND_BUZ
            '////////////////////////////////////////////////////////////////////////////
            ' Device has played it's buzz sound - ignore
            '////////////////////////////////////////////////////////////////////////////

        Case TibboDevice.DEVICE_COMMAND_FSH
            '////////////////////////////////////////////////////////////////////////////
            ' Device flashed it's LEDs - ignore
            '////////////////////////////////////////////////////////////////////////////

        Case TibboDevice.DEVICE_COMMAND_AIP
            '////////////////////////////////////////////////////////////////////////////
            ' Device replies with it's actual ip address
            '////////////////////////////////////////////////////////////////////////////
            Dim s() As String = sMessage.Split(TIBBO_MESSAGE_REPLY_DIVIDER)
            Dim sResult As String = ""

            ' TODO - do something with the result

        Case TibboDevice.DEVICE_COMMAND_CBC
            '////////////////////////////////////////////////////////////////////////////
            ' Device replies with it's current box count
            '////////////////////////////////////////////////////////////////////////////
            Dim s() As String = sMessage.Split(TIBBO_MESSAGE_REPLY_DIVIDER)
            Dim sResult As String = ""

            ' TODO - do something with the result

        Case TibboDevice.DEVICE_COMMAND_STP
            '////////////////////////////////////////////////////////////////////////////
            ' Device has been stopped - won't reply. Only way to bring it back to life
            ' is to press the 'reset' button on the actual unit - ignore
            '////////////////////////////////////////////////////////////////////////////
    End Select

End Sub

Protected Overridable Overloads Sub Dispose(disposing As Boolean)
    If (disposing) Then
        ' free managed objects
        '_ReceivingClient = Nothing
        _bClosing = True
    End If
End Sub

Public Overloads Sub Dispose() Implements IDisposable.Dispose
    Dispose(True)
    GC.SuppressFinalize(Me)
End Sub


End Class

现在,我在主窗体中所做的就是:当我的监听器窗体关闭时 - 我想关闭监听器(显然)......为此,我使用Dispose()。但是,当有人想要再次启动时,byData = _ReceivingClient.Receive(epEndPoint)程序中的SitAndReceive行会发生异常。

由于UDP是基于事务的,并且它的套接字(理论上可能)不能处于CLOSE_WAIT状态,是什么阻止我关闭它然后立即重新启动监听器?

我必须承认我是UDP套接字的新手,但到目前为止,我发现它们很愉快,即使这个例外不会导致最终用户软件崩溃(使用简单的try / catch) ,它确实让我很感兴趣,我想了解它为什么会发生。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:16)

管理最终解决这个问题。显然,如果你想有一个以上的连接到套接字,你必须手动配置它,如下所示:

Dim endPoint = New System.Net.IPEndPoint(0, _iReceivingPort)
_ReceivingClient = New System.Net.Sockets.UdpClient()
_ReceivingClient.ExclusiveAddressUse = False
_ReceivingClient.Client.SetSocketOption(Net.Sockets.SocketOptionLevel.Socket, Net.Sockets.SocketOptionName.ReuseAddress, True)
 _ReceivingClient.Client.Bind(endPoint)

现在就行,所以我很高兴。