С#: Организуем поиск устройств с помощью UDP-пакетов

Возникла новая задача — разослать всей сети UDP-пакеты и подождать откликнувшихся на них, затем отобразить список найденных устройств. Очень полезная штука. Все бы казалось просто — пропиши клиента и пошли пакет в подсеть, собственно так и было сделано:

UdpClient udp = new UdpClient(4800);
IPAddress ipaddress = IPAddress.Parse("192.168.9.255");
IPEndPoint ipendpoint = new IPEndPoint(ipaddress, 4800);
byte[] message = "Who";
int sended = udp.Send(message, message.Length, ipendpoint);

Все работает отлично, но надо иногда искать устройства и в другой подсети, а для этого нужна маска «255.255.255.255», заменяем «192.168.9.255» на «255.255.255.255» и происходит чудо — не приходит ответ.

С помощью WireShark было установлено — что уходит пакет на первый сетевой адаптер, а нам надо на другой, сказывается Windows 8 и установленная виртуальная машина, которая в систему еще добавляет сетевых адаптеров. Пришлось дописывать программу, так чтобы она умела рассылать UDP-пакеты по всем сетевым адаптерам с TCP/IP v.4 Собственно не буду сильно расписывать, а приведу только то, что получилось:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace UDPclient
{
    public class UDPWrapper
    {
        private bool stop_service = false;
        List ar = new List();
        public List st = new List();
        public int cnt_rec = 0;
        public UDPWrapper()
        {
            IPAddress[] ips = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
            UdpClient udp_;
            foreach (IPAddress curAdd in ips)
            {
                if (curAdd.AddressFamily == AddressFamily.InterNetwork)
                {
                    IPEndPoint ipSRCpoint = new IPEndPoint(curAdd, 4800);
                    udp_ = new UdpClient(ipSRCpoint);
                    udp_.EnableBroadcast = true;
                    ar.Add(udp_);
                    ReceiveMessages(udp_);
                }
            }
        }
        public void Close()
        {
            stop_service = true;
            foreach (UdpClient udp in ar)
            {
                udp.Close();
            }
        }
        public int UDP_find()
        {
            st.Clear();
            IPAddress ipaddress = IPAddress.Parse("255.255.255.255");
            IPEndPoint ipendpoint = new IPEndPoint(ipaddress, 4800);
            byte[] message = "Who";
            foreach (UdpClient udp in ar)
            {
                udp.Send(message, message.Length, ipendpoint);
            }
            int cnt;
            do
            {
                cnt = cnt_rec;
                Thread.Sleep(200);
            } while (cnt != cnt_rec);
            return cnt_rec;
        }
        public static bool messageReceived = false;
        class UdpState
        {
            public IPEndPoint e;
            public UdpClient u;
        }
        public void ReceiveCallback(IAsyncResult ar)
        {
            UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).u;
            IPEndPoint end = (IPEndPoint)((UdpState)(ar.AsyncState)).e;
            try
            {
                Byte[] receiveBytes = u.EndReceive(ar, ref end);
                string receiveString = Encoding.ASCII.GetString(receiveBytes);
                st.Add(receiveString);
                cnt_rec++;
            }
            catch (Exception e)
            {
            }
            if (stop_service == false)
                ReceiveMessages(u);
        }
        public void ReceiveMessages(UdpClient u)
        {
            IPEndPoint e = new IPEndPoint(IPAddress.Any, 4800);
            UdpState s = new UdpState();
            s.e = e;
            s.u = u;
            u.BeginReceive(new AsyncCallback(ReceiveCallback), s);
        }
    }
}

А теперь как это использовать:

UDPclient.UDPWrapper udp_finder = new UDPclient.UDPWrapper();
udp_finder.UDP_find();
udp_finder.Close();
// тут парсим udp_finder.st разбия строку как нам надо

Собственно — это мой второй опыт использования C#, но думаю пригодится. Чуть позже выложу сервер, который будет ждать приема данных и отвечать на запрос.