UDP локальная трансляция

Я работаю над инструментом дистанционного управления. Клиент запускает программу для локальной отправки команд на серверы, чтобы контролировать их.

Однако клиент не знает IP-адрес сервера и наоборот.

Я решил использовать UDP-вещание (пожалуйста, скажите, есть ли лучший способ сделать это, я попытался использовать многоадресную рассылку, но я действительно не понял этого). При запуске клиент (который контролирует серверы) передает сообщение, чтобы сообщить серверам, что подключен новый клиент. Затем (или когда сервер запущен) серверы транслируют свои собственные IP-адреса. Когда клиент получает IP-адрес, он пытается подключиться через TCP.

К сожалению, у меня возникли проблемы с этим. Я случайно получил An existing connection was forcibly closed by the remote host исключения, и я не смог выяснить, почему. Исключение произошло в моей клиентской программе при прослушивании UDP-трансляций.

Сейчас я ищу лучший способ найти клиентов. Должен ли я использовать широковещательный или многоадресный? Как бы я это реализовал?

РЕДАКТИРОВАТЬ: не было бы проблемой использовать несколько портов. Тем не менее, я должен иметь возможность запустить клиент и сервер на одном компьютере.


Вот код, который я использовал

Клиент (контролирует серверы)

'Variables
    Private UdpBroadcaster As UdpClient
    Private UdpBroadcasterEndpoint As New IPEndPoint(IPAddress.Broadcast, 4334)


'Sub New()
    Try
        UdpBroadcaster = New UdpClient(4333)
        UdpBroadcaster.EnableBroadcast = True
    Catch
        MsgBox("Error creating UDP client! Port already in use?", ...)
    End Try

'Called when the application starts
Private Sub StartUdpListener()
    Dim ListenerUdp As New Thread(AddressOf UdpListener)
    ListenerUdp.IsBackground = True
    ListenerUdp.Start()
End Sub

'Started as thread in StartUdpListener()
Private Sub UdpListener()
    Try
        Do
            'The next line fails with the error I described (An existing connection was forcibly closed by the remote host)
            Dim ReceivedBytes() As Byte = UdpBroadcaster.Receive(UdpBroadcasterEndpoint)
            Dim ReceivedString As String = System.Text.Encoding.UTF32.GetString(ReceivedBytes)

            'The following three lines will just connect to the received hostname
            Dim ScanThread As New Thread(Sub() ScanSingle(ReceivedString))
            ScanThread.IsBackground = True
            ScanThread.Start()
        Loop
    Catch
        If Not UdpBroadcaster Is Nothing Then
            UdpBroadcaster.Close()
            UdpBroadcaster = Nothing
        End If

        InvokeStatus("UDP connection lost, please try again later.")
    End Try
End Sub

'Called when the application starts and when the user manually clicks the "UDP Scan" button
Private Sub StartBroadcastUdpThread()
    Dim UdpBroadcastThread As New Thread(Sub() BroadcastUdp())
    UdpBroadcastThread.IsBackground = True
    UdpBroadcastThread.Start()
End Sub

'Started as thread in StartBroadcastUdpThread()
Private Sub BroadcastUdp()
    If UdpBroadcaster Is Nothing Then
        Try
            UdpBroadcaster = New UdpClient(4333)
            UdpBroadcaster.EnableBroadcast = True
        Catch
            MsgBox("Error creating UDP Client.", MsgBoxStyle.Critical, "Error")
            Application.Exit()
            Return
        End Try
    End If

    Dim BroadcastBytes() As Byte = System.Text.Encoding.UTF32.GetBytes("Client-Identify")
    UdpBroadcaster.Send(BroadcastBytes, BroadcastBytes.Length, UdpBroadcasterEndpoint)
    InvokeStatus("UDP request sent successfully")
End Sub



Серверы (контролируются клиентом)

'Variables
Private UdpBroadcaster As UdpClient
Private UdpBroadcasterEndpoint As New IPEndPoint(IPAddress.Broadcast, 4333)

'Main method
Public Sub Main()
    Try
        UdpBroadcaster = New UdpClient(4334)
        UdpBroadcaster.EnableBroadcast = True

        StartUdpListener()
        StartBroadcastUdpThread()
    Catch
        Console.WriteLine("Failed to create server. Port already in use?")
    End Try
End Sub

'Called in Main()
Private Sub StartUdpListener()
    Dim ListenerUdp As New Thread(AddressOf UdpListener)
    ListenerUdp.IsBackground = True
    ListenerUdp.Start()
End Sub

'Started as thread in StartUdpListener()
Private Sub UdpListener()
    Try
        Do
            Dim ReceivedBytes() As Byte = UdpBroadcaster.Receive(UdpBroadcasterEndpoint)
            Dim ReceivedString As String = System.Text.Encoding.UTF32.GetString(ReceivedBytes)

            If ReceivedString.Equals("Client-Identify") Then
                StartBroadcastUdpThread()
            End If
        Loop
    Catch
        If Not UdpBroadcaster Is Nothing Then
            UdpBroadcaster.Close()
        End If
    End Try
End Sub

'Called when the application is started or a "Client-Identify" command is received
Private Sub StartBroadcastUdpThread()
    Dim UdpBroadcastThread As New Thread(Sub() BroadcastUdp())
    UdpBroadcastThread.IsBackground = True
    UdpBroadcastThread.Start()
End Sub

'Started as thread in StartBroadcastUdpThread()
Private Sub BroadcastUdp()
    Dim BroadcastBytes() As Byte = System.Text.Encoding.UTF32.GetBytes(Dns.GetHostName)
    UdpBroadcaster.Send(BroadcastBytes, BroadcastBytes.Length, UdpBroadcasterEndpoint)
End Sub

Заранее спасибо!

3 ответа

Спасибо за ответ. Я исправил это, удалив функцию для ручного вызова StartBroadcastUdpThread() в моем клиенте.

Я до сих пор не понимаю, почему это происходит. Я использую один и тот же код как для клиента, так и для сервера, за исключением перестановки портов. TCP-сервер не падает, даже если StartBroadcastUdpThread() метод вызывается несколько раз, клиент делает. Кстати, проблема возникает независимо от того, был ли клиент или сервер запущен первым.

Даже если я не совсем понимаю, почему вещание во второй раз мешает клиенту принимать трансляции - это пока исправлено. Спасибо за помощь!

Минимальная база UDP-сервера:

Imports System.Threading

Shared client As UdpClient
Shared receivePoint As IPEndPoint

client = New UdpClient(2828) 'Port
receivePoint = New IPEndPoint(New IPAddress(0), 0)

Dim readThread As Thread = New Thread(New ThreadStart(AddressOf WaitForPackets))
readThread.Start()

Public Shared Sub WaitForPackets()
    While True
        Dim data As Byte() = client.Receive(receivePoint)
        Console.WriteLine("=" + System.Text.Encoding.ASCII.GetString(data))
    End While
End Sub

Я бы предложил использовать Zeroconf для поиска сервера и клиентов, а затем использовать сокет TCP для связи между ними. Вы можете увидеть пример реализации для объявлений пары ключ-значение zeroconf здесь: https://github.com/Eyescale/Lunchbox/blob/master/lunchbox/servus.cpp

Другие вопросы по тегам