Широковещательное сообщение UDP на все доступные сетевые карты

мне нужно отправить сообщение UDP на определенный IP и порт.

Так как есть 3 сетевые карты,

10.1.x.x
10.2.x.x
10.4.x.x

когда я отправляю сообщение UDP, я получаю сообщение только в одном сетевом адаптере...остальные ip-адреса не принимаются.

Я хочу проверить сетевой адаптер при отправке сообщения. Как я могу это сделать?


В настоящее время я использую следующее:

IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(LocalIP), 0);
IPEndPoint targetEndPoint = new IPEndPoint(TargetIP, iTargetPort);
UdpClient sendUdpClient = new UdpClient(localEndPoint);
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

4 ответов


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

public class MyUdpClient : UdpClient
{
   public MyUdpClient() : base()
   {
      //Calls the protected Client property belonging to the UdpClient base class.
      Socket s = this.Client;
      //Uses the Socket returned by Client to set an option that is not available using UdpClient.
      s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
      s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
   }

   public MyUdpClient(IPEndPoint ipLocalEndPoint) : base(ipLocalEndPoint)
   {
      //Calls the protected Client property belonging to the UdpClient base class.
      Socket s = this.Client;
      //Uses the Socket returned by Client to set an option that is not available using UdpClient.
      s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
      s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
   }

}

затем, чтобы отправить UDP пакет через эфир, я использую что-то вроде следующего. Я использую IPAddress.Broadcast и MyUdpClient, которое отличается от вашего кода.

IPEndPoint  localEndPoint  = new IPEndPoint(IPAddress.Parse(LocalIP), 0);
IPEndPoint  targetEndPoint = new IPEndPoint(IPAddress.Broadcast, iTargetPort);
MyUdpClient sendUdpClient  = new MyUdpClient(localEndPoint);
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

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

Итак,в вашем примере используется одноадресная передача. Вам нужно установить LocalIP на IP-адрес локального интерфейса, который вы хотите отправить. С тремя интерфейсами у вас будет три локальных IP-адреса, и вам нужно выбрать правильный для использования.

IPEndPoint  localEndPoint  = new IPEndPoint(IPAddress.Parse(LocalIP), 0);
IPEndPoint  targetEndPoint = new IPEndPoint(TargetIP, iTargetPort);
MyUdpClient sendUdpClient  = new MyUdpClient(localEndPoint);
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

поскольку маршрут выключен, вы можете увидеть его на всех интерфейсах, но вам нужно будет проверить это для случая одноадресной рассылки.

если вы не заботитесь о отправить IP или порт, вы можете использовать следующий код.

IPEndPoint  targetEndPoint = new IPEndPoint(TargetIP, iTargetPort);
MyUdpClient sendUdpClient  = new MyUdpClient();
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

или для трансляции

IPEndPoint  targetEndPoint = new IPEndPoint(IPAddress.Broadcast, iTargetPort);
MyUdpClient sendUdpClient  = new MyUdpClient();
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

проблема с IPAddress.Broadcast это то, что они не будут проходить через какие-либо шлюзы. Чтобы обойти это, вы можете создать список IPAddresses а затем цикл и отправить. Кроме того, поскольку отправка может завершиться ошибкой для сетевых проблем, которые вы не можете контролировать, у вас также должен быть блок try/catch.

ArrayList ip_addr_acq = new ArrayList();

ip_addr_acq.Add(IPAddress.Parse("10.1.1.1")); // add to list of address to send to

try
{
   foreach (IPAddress curAdd in ip_addr_acq) 
   {
       IPEndPoint  targetEndPoint = new IPEndPoint(curAdd , iTargetPort);
       MyUdpClient sendUdpClient  = new MyUdpClient();
       int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

       Thread.Sleep(40); //small delay between each message
    }
 }
 catch
 {
 // handle any exceptions
 }

Edit: см. выше изменение одноадресной рассылки с несколькими интерфейсами, а также проблема с попыткой одноадресной рассылки пакетов в доступные сети.


расширение ответа Рекса. Это позволяет вам не нужно жестко кодировать ip-адреса,которые вы хотите транслировать. Циклы через все интерфейсы, проверяет, если они вверх, удостоверяется, что он имеет информацию IPv4, и IPv4-адрес связан с ним. Просто измените переменную " data "на любые данные, которые вы хотите транслировать, а порт" target " на тот, который вы хотите. Небольшой недостаток в том, что если интерфейс имеет несколько IP-адресов, связанных с ним, он будет вещать из каждого адрес. Примечание: это также попытается отправить трансляции через любой VPN-адаптер (через сеть и Центр обмена/Сетевые подключения, Win 7+ verified), и если вы хотите получать ответы, вам придется сохранить все клиенты. Вам также не понадобится вторичный класс.

    foreach( NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces() ) {
        if( ni.OperationalStatus == OperationalStatus.Up && ni.SupportsMulticast && ni.GetIPProperties().GetIPv4Properties() != null ) {
            int id = ni.GetIPProperties().GetIPv4Properties().Index;
            if( NetworkInterface.LoopbackInterfaceIndex != id ) {
                foreach(UnicastIPAddressInformation uip in ni.GetIPProperties().UnicastAddresses ) {
                    if( uip.Address.AddressFamily == AddressFamily.InterNetwork ) {
                        IPEndPoint local = new IPEndPoint(uip.Address.Address, 0);
                        UdpClient udpc = new UdpClient(local);
                        udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
                        udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
                        byte[] data = new byte[10]{1,2,3,4,5,6,7,8,9,10};
                        IPEndPoint target = new IPEndPoint(IPAddress.Broadcast, 48888);
                        udpc.Send(data,data.Length, target);
                    }
                }
            }
        }
    }

Если вы отправляете на определенный IP-адрес, то вы одноадресный, а не широковещательный.


Я решил эту проблему, отправив UDP-трансляцию с каждого адаптера (используя bind):

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