Рассылка сообщения 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);

person Anuya    schedule 08.07.2009    source источник
comment
Можете ли вы уточнить, что вы хотите попытаться сделать.   -  person Simeon Pilgrim    schedule 08.07.2009
comment
Мое приложение будет отправлять сообщения приложению в более чем 10 системах. В котором все системы находятся в трех разных сетевых картах. Например, 10.1.x.x / 10.2.x.x / 10.4.x.x я могу получить сообщение только на одну сетевую карту 10.1.x.x, но не на две другие сетевые карты. Поэтому я хочу проверить доступность сетевой карты, а затем отправить сообщение. Спасибо.   -  person Anuya    schedule 08.07.2009
comment
так сообщение предназначено только для проверки доступности сети или оно имеет какую-то другую полезную нагрузку/значение?   -  person Simeon Pilgrim    schedule 08.07.2009
comment
Если вы пытаетесь получить избыточность, для случая, когда отправка подключена к сети A, но получатель отключен от A, но подключен к B и C (из-за сбоя сетевой карты/кабеля), и вы хотите, чтобы ваше сообщение прибыло в получатель, затем отправляющий его, как говорит Рекс, должен добиться цели, но вам придется управлять дубликатом и, возможно, неправильным обращением на принимающей стороне.   -  person Simeon Pilgrim    schedule 08.07.2009
comment
Если вы просто хотите проверить подключение каждого хоста во всех сетях, отправьте одноадресное сообщение на сетевой адрес хоста в каждой сети A, B и C.   -  person Simeon Pilgrim    schedule 08.07.2009
comment
когда я попытался подключиться к сетевому адаптеру A, он отлично работает. когда я пытался подключиться к сетевому адаптеру B, ошибка: была предпринята попытка операции с сокетом на недостижимом хосте. когда я пытался подключиться к сетевому адаптеру C, ошибка: была предпринята попытка операции сокета на недостижимом хосте.   -  person Anuya    schedule 08.07.2009


Ответы (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
 }

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

person Rex Logan    schedule 08.07.2009
comment
Привет, Рекс Логан, когда я пробовал ваш код, я получаю следующую ошибку: была предпринята попытка операции с сокетом на недостижимом хосте. Я тут запутался, сетевой адаптер с таким IP существует. когда я отлаживал строку ниже... int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint); я видел targetEndPoint = {10.4.1.2:1982}. когда targetEndPoint {10.1.1.1:1982}, я получаю пакет на удаленной машине. :( - person Anuya; 08.07.2009
comment
Что произойдет, если вы отправите на IPAddress.Broadcast? Вы также можете попробовать упрощенные версии, которые я добавил. - person Rex Logan; 08.07.2009
comment
Вы также можете попробовать закомментировать s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.DontRoute, 1); или s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.Broadcast, 1), чтобы узнать, что произойдет. - person Rex Logan; 08.07.2009
comment
Это то же самое. смог найти этот IP. Я попытался пропинговать 10.4.1.2, но он говорит, что хост назначения недоступен. Итак, теперь это проблема сети или все еще проблема приложения? Спасибо. - person Anuya; 08.07.2009
comment
когда я делаю ipaddress.broadcast, я не получаю пакет на удаленной машине. - person Anuya; 08.07.2009

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

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;
}
person merce_00    schedule 19.06.2017
comment
С некоторыми небольшими изменениями это сработало для моих нужд, спасибо! 1) вам нужно будет переместить внешний блок Try/Catch внутрь ForEach UnicastAddr и добавить блок finally, чтобы избавиться от объекта bcSocket, поскольку в некоторых случаях привязки будут вызываться. 2) Квалификация только для NetworkInterfaceType.Ethernet будет игнорировать некоторые распространенные аппаратные средства, такие как беспроводные карты и т. д. (NetworkInterfaceType.Wireless80211) - person bakerhillpins; 22.03.2019

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

    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);
                    }
                }
            }
        }
    }
person Rahly    schedule 25.09.2016

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

person Simeon Pilgrim    schedule 08.07.2009
comment
О да, это правильно. Но я должен выбрать сеть перед отправкой, верно? - person Anuya; 08.07.2009
comment
Ах, вы пытаетесь отправить пакет с каждой локальной сетевой карты на удаленный хост с одной сетевой картой? Если вы пытаетесь протестировать каждую сетевую карту, найдите хост в этой сети, чтобы отправить сообщение. Позвольте ОС выполнить маршрутизацию за вас. - person Simeon Pilgrim; 08.07.2009
comment
Но ОС всегда роутит на первую сетевую карту 10.1.x.x - person Anuya; 08.07.2009
comment
Если вы хотите отправить разные Nic'ы, отправьте на разные адреса хостов. Это если они все в трех сетях. Это сводится к тому, чего вы пытаетесь достичь, тестируя сетевое подключение или отправляя фактическое сообщение более высокого уровня. - person Simeon Pilgrim; 08.07.2009