Как сделать обнаружение сети с помощью широковещательной передачи UDP

Я хочу сделать обнаружение сети с помощью UDP Broadcast в С#. Я не знаю, как это сделать. Можете ли вы дать мне совет, как это сделать?

Я хочу сделать это учебник.

Ответ 1

Очень просто сделать то же самое в С#

Сервер:

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

Ответ 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, нет гарантии, что пакет будет доставлен.

Ответ 3

У меня был тот же вопрос, но это было не так просто для меня, как ответ, который предлагает @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;
}

Ответ 4

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

Исправить для комментария Илья Суздальницкого (блокирует второй вызов 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();
}

Поскольку после каждого ответа сервер закрывается и воссоздается, он может работать бесконечно без блокировки.

Ответ 5

Для рабочего примера посмотрите этот проект:https://github.com/xmegz/MndpTray

Сервер периодически отправляет широковещательные сообщения. Клиентская сторона получает и обрабатывает их. Многие сведения о хосте (версия ОС, IP-адрес, сетевой интерфейс и т.д.) Отправляют с ошибками.