如何使用 UDP 广播进行网络发现
本文关键字:网络 发现 广播 何使用 UDP | 更新日期: 2023-09-27 17:56:46
我想在 C# 中使用 UDP 广播进行网络发现。我不知道该怎么做。你能给我建议怎么做吗?
我想像本教程一样做。
在 C# 中做同样的事情非常简单
服务器:
var Server = new UdpClient(8888);
var ResponseData = Encoding.ASCII.GetBytes("SomeResponseData");
while (true)
{
var ClientEp = new IPEndPoint(IPAddress.Any, 0);
var ClientRequestData = Server.Receive(ref ClientEp);
var ClientRequest = Encoding.ASCII.GetString(ClientRequestData);
Console.WriteLine("Recived {0} from {1}, sending response", ClientRequest, ClientEp.Address.ToString());
Server.Send(ResponseData, ResponseData.Length, ClientEp);
}
客户:
var Client = new UdpClient();
var RequestData = Encoding.ASCII.GetBytes("SomeRequestData");
var ServerEp = new IPEndPoint(IPAddress.Any, 0);
Client.EnableBroadcast = true;
Client.Send(RequestData, RequestData.Length, new IPEndPoint(IPAddress.Broadcast, 8888));
var ServerResponseData = Client.Receive(ref ServerEp);
var ServerResponse = Encoding.ASCII.GetString(ServerResponseData);
Console.WriteLine("Recived {0} from {1}", ServerResponse, ServerEp.Address.ToString());
Client.Close();
下面是一个不同的无服务器解决方案。我需要让一堆覆盆子小童在网络上相互了解,但不能保证谁会活跃。所以这种方法让每个人都成为客户!完整的库可在 GitHub 上找到(免责声明:我创建),这使得整个过程对于 UWP 应用程序来说非常容易。
https://github.com/mattwood2855/WindowsIotDiscovery
此解决方案假定设备名称是唯一的,并且您希望使用 JSON 字符串作为通信协议,但您可以轻松地发送任何其他格式。此外,在实践中尝试捕获所有;)
一般机制:
发现您的地址
public string IpAddress
{
get
{
var hosts = NetworkInformation.GetHostNames();
foreach (var host in hosts)
{
if (host.Type == HostNameType.Ipv4) return host.DisplayName;
}
return "";
}
}
设置侦听器
var udpPort = "1234";
var socket = new DatagramSocket();
socket.MessageReceived += ReceivedDiscoveryMessage;
await socket.BindServiceNameAsync(udpPort);`
处理传入数据
async void ReceivedDiscoveryMessage(DatagramSocket socket, DatagramSocketMessageReceivedEventArgs args)
{
// Get the data from the packet
var result = args.GetDataStream();
var resultStream = result.AsStreamForRead();
using (var reader = new StreamReader(resultStream))
{
// Load the raw data into a response object
var potentialRequestString = await reader.ReadToEndAsync();
// Ignore messages from yourself
if (args.RemoteAddress.DisplayName == IpAddress) return;
// Get the message
JObject jRequest = JObject.Parse(potentialRequestString);
// Do stuff with the data
}
}
发送消息
public async void SendDataMessage(string discoveryMessage)
{
// Get an output stream to all IPs on the given port
using (var stream = await socket.GetOutputStreamAsync(new HostName("255.255.255.255"), udpPort))
{
// Get a data writing stream
using (var writer = new DataWriter(stream))
{
// Write the string to the stream
writer.WriteString(discoveryMessage);
// Commit
await writer.StoreAsync();
}
}
}
这个想法是发送包含您的IP地址和名称的发现消息。然后在接收消息功能中,将 IP 名称对添加到设备列表中。添加一些逻辑以避免重复,并在给定名称的 ip 更改时更新 IP 地址。
作为奖励,您可以让每个设备发送他们知道的设备列表。这允许您通过在发件人知道您时不响应来最小化 udp 流量。您甚至可以让接收器将列表与他们自己的列表进行比较,以发现其他设备。
冗余是UDP的朋友,不能保证数据包会被传送。
它很旧,但有人可能仍然需要这个......接受的答案很棒,但是在服务器端进行此小调整会更好。
修复了 Ilya Suzdalnitski 评论(锁定在第二个客户端上接收呼叫):
var responseData = Encoding.ASCII.GetBytes("someData");
while (true)
{
var server = new UdpClient(8888);
var clientEp = new IPEndPoint(IPAddress.Any, 0);
var clientRequestData = server.Receive(ref clientEp);
var clientRequest = Encoding.ASCII.GetString(clientRequestData);
Console.WriteLine($"Recived {clientRequest} from {clientEp.Address}, sending
response: {responseData}");
server.Send(responseData, responseData.Length, clientEp);
server.Close();
}
因为每次响应后,服务器都会关闭并重新创建,因此它可以无休止地工作而不会锁定。
我有同样的问题,但这对我来说并不容易,因为@rufanov建议的答案。
这里有一些情况我有:
- 由于我的应用程序在具有多个网络接口的计算机中正常运行,因此我遇到了广播消息仅在其中一个适配器中发送的问题。为了解决这种情况,我必须首先获取所有网络适配器列表,然后逐个发送广播消息并接收应答消息。 请务必
- 将正确的 localIpEndPoint 绑定到适配器的 IP 地址,否则发送时广播地址将出现问题。
经过一些研究和工作,我得到了这个解决方案。此代码对应于服务器端,并将通过网络发现所有应答 braodcast 消息的设备。
public static void SNCT_SendBroadcast(out List<MyDevice> DevicesList)
{
DevicesList = new List<MyDevice>();
byte[] data = new byte[2]; //broadcast data
data[0] = 0x0A;
data[1] = 0x60;
IPEndPoint ip = new IPEndPoint(IPAddress.Broadcast, 45000); //braodcast IP address, and corresponding port
NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); //get all network interfaces of the computer
foreach (NetworkInterface adapter in nics)
{
// Only select interfaces that are Ethernet type and support IPv4 (important to minimize waiting time)
if (adapter.NetworkInterfaceType != NetworkInterfaceType.Ethernet) { continue; }
if (adapter.Supports(NetworkInterfaceComponent.IPv4) == false) { continue; }
try
{
IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
foreach (var ua in adapterProperties.UnicastAddresses)
{
if (ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
//SEND BROADCAST IN THE ADAPTER
//1) Set the socket as UDP Client
Socket bcSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //broadcast socket
//2) Set socker options
bcSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
bcSocket.ReceiveTimeout = 200; //receive timout 200ms
//3) Bind to the current selected adapter
IPEndPoint myLocalEndPoint = new IPEndPoint(ua.Address, 45000);
bcSocket.Bind(myLocalEndPoint);
//4) Send the broadcast data
bcSocket.SendTo(data, ip);
//RECEIVE BROADCAST IN THE ADAPTER
int BUFFER_SIZE_ANSWER = 1024;
byte[] bufferAnswer = new byte[BUFFER_SIZE_ANSWER];
do
{
try
{
bcSocket.Receive(bufferAnswer);
DevicesList.Add(GetMyDevice(bufferAnswer)); //Corresponding functions to get the devices information. Depends on the application.
}
catch { break; }
} while (bcSocket.ReceiveTimeout != 0); //fixed receive timeout for each adapter that supports our broadcast
bcSocket.Close();
}
}
}
catch { }
}
return;
}
有关工作示例,请参阅 project:https://github.com/xmegz/MndpTray
服务器定期发送广播消息。客户端接收并处理它们。许多主机信息(操作系统版本、IP 地址、网络接口等)发送 trought。UDP 广播 CDP LLDP