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