vb.net UDP SocketException - 通常每个套接字地址只允许使用一次

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17782957/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 14:20:09  来源:igfitidea点击:

UDP SocketException - Only one usage of each socket address is normally permitted

vb.netsocketsudp

提问by fantasitcalbeastly

As much as there are many questions on here that are very similar, none of the supplied answers have helped me, which makes me sad :(

尽管这里有许多非常相似的问题,但提供的答案都没有帮助我,这让我感到难过:(

I've got a very large management system that I've been tasked to write some UDP packet sending/receiving for. I'd already written a prototype and all was well, so I started merging my code into said system. However, I've now got a (not show-stopping, but annoying) SocketException popping up:

我有一个非常大的管理系统,我的任务是编写一些 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

Here is the UDPReceiver class - Which is responsible for just sitting in a loop and waiting for responses from the Device Servers we've got dotted about the place.

这是 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

Now, all I'm doing in the main form, is: When my listener form is closed - I want to close the listener down (obviously)... For this, I'm using Dispose(). However, when someone wants to start it up again, said exception occurs on the byData = _ReceivingClient.Receive(epEndPoint)line in the SitAndReceiveprocedure.

现在,我在主窗体中所做的就是:当我的监听器窗体关闭时 - 我想关闭监听器(显然)......为此,我使用Dispose(). 然而,当有人想再次启动,表示对出现异常,byData = _ReceivingClient.Receive(epEndPoint)在该行SitAndReceive的过程。

Since UDP is transaction based and its sockets (possibly theoretically) can't be in a CLOSE_WAIT state, what is stopping me from closing it down and then immediately relaunching the listener?

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

I must admit I'm new to UDP sockets, so far however, I've found them a joy to work with and even though this exception wouldn't crash an end-users software (with a simple try/catch), it does have me intrigued and I'd like to understand why it's happening.

我必须承认我是 UDP 套接字的新手,但是到目前为止,我发现使用它们是一种乐趣,即使此异常不会使最终用户软件崩溃(通过简单的 try/catch),它也确实如此让我很感兴趣,我想了解它为什么会发生。

Any help is very much appreciated.

很感谢任何形式的帮助。

回答by fantasitcalbeastly

Managed to figure this out in the end. Apparently if you want to have more than one connection to a socket, you have to manually configure it, like so:

最终设法解决了这个问题。显然,如果您想与一个套接字建立多个连接,则必须手动配置它,如下所示:

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)

Works now, so I'm happy.

现在工作,所以我很高兴。