【问题标题】:How to do Network discovery using UDP broadcast如何使用 UDP 广播进行网络发现
【发布时间】:2014-05-16 04:13:11
【问题描述】:

我想在 C# 中使用 UDP 广播进行网络发现。我不知道该怎么做。你能给我一些建议吗?

我想这样做tutorial

【问题讨论】:

标签: c# udp


【解决方案1】:

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

【讨论】:

  • 它只是将一个带有指定数据的UDP包发送到255.255.255.255 - 到本地网络的广播地址。如果您的计算机和服务器之间没有任何“智能硬件”(如路由器),那么它必须响应此包。迭代所有网络对于意见来说是多余的 - 大多数发送的包无论如何都不会被传递..但是可以通过迭代 NetworkInterface.GetAllNetworkInterfaces() 结果来做同样的事情
  • 感谢 Rufanov 成功了!但我认为有一个错误 var ServerResponseData = Server.Receive(ref ServerEp);应该是 var ServerResponseData = Client.Receive(ref ServerEp);对吗?
  • 很好的答案!但是有一个问题 - 它锁定了第二个 Client.Receive 调用,因为它只收到来自服务器的一个响应,并等待第二个。
  • 那么我该如何解决这个问题_?
【解决方案2】:

这是一个不同的无服务器解决方案。我需要让一堆树莓派在网络上相互了解,但不能保证谁会活跃。所以这种方法可以让每个人都成为客户! GitHub 上提供了完整的库(免责声明:我创建的),这使得整个过程对于 UWP 应用程序来说非常容易。

https://github.com/mattwood2855/WindowsIotDiscovery

此解决方案假定设备名称是唯一的,并且您希望使用 JSON 字符串作为通信协议,但您可以轻松地发送任何其他格式。此外,在实践中尝试捕获所有内容;)

一般机制:

发现你的 IP 地址

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-name 对添加到设备列表中。添加一些逻辑以避免重复,如果给定名称的 IP 更改,则更新 IP 地址。

作为奖励,您可以让每台设备发送他们知道的设备列表。这允许您通过在发件人知道您时不响应来最小化 udp 流量。您甚至可以让接收者将列表与他们自己的列表进行比较以发现其他设备。

冗余是您与 UDP 的朋友,无法保证数据包会被传递。

【讨论】:

  • 看起来很有趣,我可能很快就会尝试使用它来连接负载均衡器后面的 Azure VMSS 上运行的一组 Web API。我一定会在这样做时提供反馈。
【解决方案3】:

我知道它已经过时了,但有人可能仍然需要这个...已接受的答案很棒,但在服务器端进行这个小调整会更好。

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

因为每次响应后服务器都会关闭并重新创建,因此它可以无休止地工作而无需锁定。

【讨论】:

    【解决方案4】:

    我有同样的问题,但对我来说并不像@rufanov 建议的答案那么容易。

    这是我遇到的一些情况:

    • 由于我的应用程序在具有多个网络接口的计算机上正常运行,因此我遇到了广播消息仅在其中一个适配器中发送的问题。为了解决这种情况,我必须首先获取所有网络适配器列表,然后逐个发送广播消息和接收应答消息。
    • 务必将正确的 localIpEndPoint 绑定到您的适配器 IP 地址,否则您将在发送时遇到广播地址问题。

    经过一些研究和工作,我得到了这个解决方案。此代码对应于服务器端,将发现所有响应广播消息的设备的网络。

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

    【讨论】:

      【解决方案5】:

      对于工作示例,请参阅该项目:https://github.com/xmegz/MndpTray

      服务器定期发送广播消息。客户端接收并处理它们。许多主机信息(操作系统版本、IP 地址、网络接口等)都已发送。

      【讨论】:

        猜你喜欢
        • 2011-09-16
        • 2021-06-29
        • 1970-01-01
        • 2010-10-10
        • 1970-01-01
        • 2016-07-30
        • 1970-01-01
        • 2021-11-19
        • 2010-11-20
        相关资源
        最近更新 更多