简体   繁体   English

如何使用UDP广播进行网络发现

[英]How to do Network discovery using UDP broadcast

I want to to do network discovery using UDP Broadcast in C#. 我想在C#中使用UDP Broadcast进行网络发现。 I don't know how to do this. 我不知道该怎么做。 Can you give me advice on how to do it? 你能告诉我怎么做的建议吗?

I want to do like this tutorial . 我想做这个教程

It's very simple to make same thing in C# 在C#中做同样的事情非常简单

Server: 服务器:

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);
}

Client: 客户:

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();

Here is a different solution that is serverless. 这是一个无服务器的不同解决方案。 I had a need to have a bunch of raspberry pis be aware of each other on a network, but had no guarantees of who would be active. 我需要让一堆覆盆子在网络上相互了解,但不能保证谁会活跃。 So this approach allows everyone to be a client! 所以这种方法可以让每个人都成为客户! The complete library is available on GitHub (disclaimer: I created) and that makes this whole process really reaaaally easy for UWP apps. 完整的库可以在GitHub上找到(免责声明:我创建),这使得整个过程对于UWP应用来说真的很容易。

https://github.com/mattwood2855/WindowsIotDiscovery https://github.com/mattwood2855/WindowsIotDiscovery

This solution assumes that device names are unique and that you want to use JSON strings as the communication protocol, but you could easily just send any other format. 此解决方案假设设备名称是唯一的,并且您希望使用JSON字符串作为通信协议,但您可以轻松地发送任何其他格式。 Also, in practice try-catch everything ;) 此外,在实践中尝试捕捉一切;)

The general mechanism: 一般机制:

Discover your IpAdress 发现你的IpAdress

public string IpAddress
{
    get
    {
        var hosts = NetworkInformation.GetHostNames();
        foreach (var host in hosts)
        {
            if (host.Type == HostNameType.Ipv4) return host.DisplayName;    
        }
        return "";
    }
}

Set up your listener 设置你的听众

var udpPort = "1234";
var socket = new DatagramSocket();
socket.MessageReceived += ReceivedDiscoveryMessage;
await socket.BindServiceNameAsync(udpPort);`

Handle incoming data 处理传入的数据

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
    }
}

Send a message 发送一个消息

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();
        }
    }
}

The idea would be to send a discovery message containing your ip address and name. 想法是发送包含您的IP地址和名称的发现消息。 Then in the receive message function add the ip-name pairs to a List of devices. 然后在接收消息功能中将ip-name对添加到设备列表中。 Add a little logic to avoid duplicates and update Ip address if the ip changes for a given name. 添加一点逻辑以避免重复,并在ip更改给定名称时更新IP地址。

As a bonus, you can have each device send the list of devices they know about. 作为奖励,您可以让每台设备发送他们了解的设备列表。 This allows you to minimize udp traffic by not responding when the sender is aware of you. 这允许您通过在发件人意识到您时不响应来最小化udp流量。 You can even have the receiver compare the list against their own list to discover other devices. 您甚至可以让接收器将列表与其自己的列表进行比较以发现其他设备。

Redundancy is your friend with UDP, there is no guarantee that a packet will be delivered. 冗余是您与UDP的朋友,无法保证将传送数据包。

I had the same question but it was not that easy for me as the answer that @rufanov suggests. 我有同样的问题但对我来说并不是那么容易,因为@rufanov建议的答案。

Here some situation I had: 我有一些情况:

  • Since my application is running normally in a computer that has several network interfaces, I had the problem that the broadcast message was sent only in one of the adapters. 由于我的应用程序在具有多个网络接口的计算机中正常运行,因此我遇到了广播消息仅在其中一个适配器中发送的问题。 To solve this situation I had to get first all the network adapter list and go one by one sending the broadcast message and receiving the answer message. 为了解决这种情况,我必须首先获得所有网络适配器列表,然后逐个发送广播消息并接收应答消息。
  • It is important that you bind the correct localIpEndPoint to your adapters ip address, otherwise you will have problems with the broadcast address by sending. 将正确的localIpEndPoint绑定到适配器的IP地址非常重要,否则通过发送将导致广播地址出现问题。

After some reserch and work I got to this solution. 经过一些研究和工作后,我得到了这个解决方案。 This code corresponds to the server side and will make the network discovery of all devices answering to the braodcast message. 此代码对应于服务器端,并将使所有设备的网络发现回应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;
}

I know it's old but someone may still need this...The accepted answer is great but with this little tweak on the server side it's even better. 我知道它已经老了,但有人可能仍然需要这个......接受的答案很棒,但是在服务器端进行这一小调整就更好了。

Fix for the Ilya Suzdalnitski comment (locks up on the second Client.Receive call): 修复Ilya Suzdalnitski评论(锁定第二个Client.Receive调用):

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();
}

Because after each response the server is closed and recreated, it can work endlessly without locking. 因为在每次响应之后服务器被关闭并重新创建,它可以无限制地工作而不会锁定。

For working example, see that project: https://github.com/xmegz/MndpTray 有关工作示例,请参阅该项目: https//github.com/xmegz/MndpTray

The server periodically sends broadcast messages. 服务器定期发送广播消息。 The client side receive and process them. 客户端接收并处理它们。 Many host information (Os version, IP address, Network interface, etc..) send trought. 许多主机信息(Os版本,IP地址,网络接口等)发送。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM