web-dev-qa-db-fra.com

Comment faire la découverte du réseau en utilisant la diffusion UDP

Je souhaite effectuer une découverte de réseau à l'aide de la diffusion UDP en C #. Je ne sais pas comment faire ça. Pouvez-vous me donner des conseils sur la façon de le faire?

Je veux faire comme ceci tutorial .

17
NTK88

C'est très simple de faire la même chose en C #

Serveur:

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

Voici une solution différente sans serveur. J'avais besoin que plusieurs pépites de framboises se connaissent sur un réseau, mais je n'avais aucune garantie quant à savoir qui serait actif. Donc, cette approche permet à chacun d’être un client! La bibliothèque complète est disponible sur GitHub (disclaimer: j'ai créé), ce qui rend ce processus vraiment très facile pour les applications UWP. 

https://github.com/mattwood2855/WindowsIotDiscovery

Cette solution suppose que les noms de périphérique sont uniques et que vous souhaitez utiliser des chaînes JSON comme protocole de communication, mais vous pouvez simplement envoyer tout autre format. Aussi, dans la pratique essayez-attrapez tout;)

Le mécanisme général:

Découvrez votre adresse IP

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

Configurez votre auditeur

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

Gérer les données entrantes

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

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

L'idée serait d'envoyer un message de découverte contenant votre adresse IP et votre nom. Ensuite, dans la fonction de réception de message, ajoutez les paires ip-name à une liste de périphériques. Ajoutez un peu de logique pour éviter les doublons et mettez à jour l'adresse IP si l'adresse IP change pour un nom donné. 

En prime, chaque appareil peut envoyer la liste des appareils qu’il connaît. Cela vous permet de minimiser le trafic udp en ne répondant pas lorsque l'expéditeur a connaissance de vous. Vous pouvez même demander au destinataire de comparer la liste avec sa propre liste pour découvrir d'autres périphériques.

La redondance est votre ami avec UDP, il n'y a aucune garantie qu'un paquet sera livré.

6
Mr Wood

J'avais la même question, mais ce n'était pas si facile pour moi, comme le suggère la réponse de @rufanov.

Voici une situation que j'ai eue:

  • Mon application fonctionnant normalement sur un ordinateur doté de plusieurs interfaces réseau, le problème suivant est que le message de diffusion n'a été envoyé que dans l'un des adaptateurs. Pour résoudre cette situation, je devais d'abord obtenir toute la liste de la carte réseau, puis envoyer un par un le message de diffusion et recevoir le message de réponse.
  • Il est important que vous associez l'adresse localeIpEndPoint correcte à l'adresse IP de votre adaptateur, sinon vous aurez des problèmes avec l'adresse de diffusion lors de l'envoi.

Après quelques recherches et travaux, je suis arrivé à cette solution. Ce code correspond au côté server et fera la découverte du réseau de tous les périphériques répondant au 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;
}
1
merce_00

Je sais que c'est vieux mais que quelqu'un peut encore en avoir besoin ... La réponse acceptée est excellente, mais avec ce petit Tweak côté serveur, c'est encore mieux.

Correction du commentaire Ilya Suzdalnitski (se bloque sur le deuxième appel 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();
}

Parce qu'après chaque réponse, le serveur est fermé et recréé, il peut fonctionner sans fin, sans verrouillage.

0
goodfellow