UDP: Чтение данных со всех сетевых интерфейсов

У меня есть следующий код для чтения многоадресного сообщения, поступающего из сети, для указанного IP + порта

private static void ReceiveMessages(int port, string ip, CancellationToken token)
{
    Task.Factory.StartNew(() =>
        {
            using (var mUdpClientReceiver = new UdpClient())
            {
                var mReceivingEndPoint = new IPEndPoint(IPAddress.Any, port);
                mUdpClientReceiver.ExclusiveAddressUse = false;
                mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                mUdpClientReceiver.ExclusiveAddressUse = false;
                mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
                mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);

                while (!token.IsCancellationRequested)
                {
                    byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);

                    Console.WriteLine("Message received from {0} ",mReceivingEndPoint);
                }
            }
        });
}

У меня есть два сетевых адаптера, с которых я получаю данные по этому многоадресному порту ip + (подтверждено двумя экземплярами wireshark, отслеживающими каждый сетевой адаптер). Я вижу на wireshark много трафика, поступающего на этот порт + IP) для обеих сетевых карт.

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

Я дважды проверил с помощью netstat, у меня нет другого программного обеспечения, прослушивающего мой порт: введите описание изображения здесь

Так почему же я получаю трафик только от одной из двух моих сетевых карт?

ИЗМЕНИТЬ:

Я даже пробовал следующее:

private static void ReceiveMessages(int port, string ip, CancellationToken token, IEnumerable<IPAddress> ipAddresses)
{
    foreach (IPAddress ipAddress in ipAddresses)
    {
        IPAddress ipToUse = ipAddress;
        Task.Factory.StartNew(() =>
        {
            using (var mUdpClientReceiver = new UdpClient())
            {

                var mReceivingEndPoint = new IPEndPoint(ipToUse, port);
                mUdpClientReceiver.ExclusiveAddressUse = false;
                mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
                mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
                mUdpClientReceiver.ExclusiveAddressUse = false;
                mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
                mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);
                Console.WriteLine("Starting to listen on "+ipToUse);
                while (!token.IsCancellationRequested)
                {
                    byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);

                    Console.WriteLine("Message received from {0} on {1}",  mReceivingEndPoint,ipToUse);
                }
            }
        });
    }
}

Я дважды вижу «Начало прослушивания правильного IP» (для моих двух IP-адресов), но по-прежнему отображаются только данные, поступающие с одной сетевой карты.

ИЗМЕНИТЬ 2

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

И я точно знаю, что у меня есть устройства, которые мне отвечают, которые подключены только к одной сети (не к обеим)

ИЗМЕНИТЬ 3

Другое дело: если я отправляю сообщение от себя (localhost) на все имеющиеся у меня сетевые карты, я вижу, что они приходят на два моих сетевых интерфейса. НО, если я запущу свою программу дважды, только первая программа получит сообщения, а не вторая.

Изменить 4

Дополнительная информация после первого комментария: у меня две сетевые карты, одна с 10.10.24.78 ip, другая с 10.9.10.234 ip. Это не я отправляю данные, а сетевые части (порт 5353 с этим ip-адресом является известным многоадресным адресом, используемым для mDNS, поэтому я должен получать трафик от таких вещей, как принтер, itunes, macs и некоторые другие части программного обеспечения, которое мы создали). Данные передаются многоадресно на ip 224.0.0.251 и порт 5353.

Вот код, который вы можете использовать для отправки данных на несколько IP-адресов, но, как я описал, если вы запускаете его локально, он почти работает (за исключением того, что только один локальный клиент получает сообщение).

private static void SendManuallyOnAllCards(int port, string multicastAddress, IEnumerable<IPAddress> ipAddresses)
{
    foreach (IPAddress remoteAddress in ipAddresses)
    {
        IPAddress ipToUse = remoteAddress;
        using (var mSendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
        {
            mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership,
                                        new MulticastOption(IPAddress.Parse(multicastAddress)));
            mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 255);
            mSendSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

            var ipep = new IPEndPoint(ipToUse, port);
            //IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(multicastAddress), port);
            mSendSocket.Bind(ipep);
            mSendSocket.Connect(ipep);


            byte[] bytes = Encoding.ASCII.GetBytes("This is my welcome message");
            mSendSocket.Send(bytes, bytes.Length, SocketFlags.None);
        }
    }
}

EDIT 5 Вот результат моей route print (не знал этой команды), и на двух моих IP-адресах я всегда получаю данные на 10.9.10.234 введите здесь описание изображения

Изменить 6

Я пробовал несколько других вещей:

  1. Используйте сокет для получения вместо UdpClient --> Не работает
  2. Установите дополнительную опцию socketOption для считывателя (DontRoute = 1, Broadcast = 1) --> Не работает
  3. Укажите MulticastInterface, который должен использовать читатель Socket (используя socketOption MulticastInterface) --> Не работает

person J4N    schedule 07.03.2013    source источник
comment
Какие у вас интерфейсы (ethernet, eth/wlan)? А какие у вас IP на этих интерфейсах? Было бы полезнее, если бы вы могли проанализировать, как вы отправляете свое многоадресное сообщение.   -  person PCoder    schedule 07.03.2013
comment
@PCoder: Спасибо за ваш комментарий, я добавил некоторые детали к своему первоначальному вопросу (Редактировать 4)   -  person J4N    schedule 07.03.2013
comment
И можете ли вы показать нам вывод вашей команды route print?   -  person PCoder    schedule 07.03.2013
comment
@PCoder: Все, что может помочь, я действительно в отчаянии (и я не эксперт по сетям :( ). Я отредактировал вопрос с результатом   -  person J4N    schedule 07.03.2013
comment
Я согласен, что ваша проблема действительно сложная, и у меня тоже нет решения :(. Я нашел очень похожую на вашу проблему, возможно, это может быть полезно. http://linux.derkeiler.com/Newsgroups/comp.os.linux.networking/2009-09/msg00223.html   -  person PCoder    schedule 07.03.2013
comment
@PCoder: В любом случае спасибо за помощь. Я думаю, что эквивалентом С# является SocketOPtion SocketOptionName.MulticastInterface, но это ничего не изменило   -  person J4N    schedule 07.03.2013


Ответы (3)


У меня была та же проблема, что я хотел получать многоадресные рассылки со всех моих сетевых интерфейсов. Как уже сказал EJP, вам нужно вызвать JoinMulticastGroup(IPAddress multicastAddr, IPAddress localAddress) на UdpClient для всех сетевых интерфейсов:

int port = 1036;
IPAddress multicastAddress = IPAddress.Parse("239.192.1.12");

client = new UdpClient(new IPEndPoint(IPAddress.Any, port));

// list of UdpClients to send multicasts
List<UdpClient> sendClients = new List<UdpClient>();

// join multicast group on all available network interfaces
NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

foreach (NetworkInterface networkInterface in networkInterfaces)
{
    if ((!networkInterface.Supports(NetworkInterfaceComponent.IPv4)) ||
        (networkInterface.OperationalStatus != OperationalStatus.Up))
    {
        continue;
    }

    IPInterfaceProperties adapterProperties = networkInterface.GetIPProperties();
    UnicastIPAddressInformationCollection unicastIPAddresses = adapterProperties.UnicastAddresses;
    IPAddress ipAddress = null;

    foreach (UnicastIPAddressInformation unicastIPAddress in unicastIPAddresses)
    {
        if (unicastIPAddress.Address.AddressFamily != AddressFamily.InterNetwork)
        {
            continue;
        }

        ipAddress = unicastIPAddress.Address;
        break;
    }

    if (ipAddress == null)
    {
        continue;
    }

    client.JoinMulticastGroup(multicastAddress, ipAddress);

    UdpClient sendClient = new UdpClient(new IPEndPoint(ipAddress, port));
    sendClients.Add(sendClient);
}

Я также создаю список клиентов UdpClient, чтобы я мог отправлять многоадресные рассылки на все сетевые интерфейсы.

person floppes    schedule 04.04.2013
comment
Спасибо, я нашел это полезным - person CybeX; 13.01.2016

Наконец-то я нашел, как это сделать!

На самом деле, если я сохраняю точно такой же код, но использую его с асинхронными методами, он работает!!! Я просто не могу понять, почему это не работает с методом синхронизации (если кто-то знает, пожалуйста, сообщите мне :))

Поскольку я потерял на этом 3 дня, я думаю, что стоит привести пример:

private static void ReceiveAsync(int port, string address, IEnumerable<IPAddress> localAddresses)
{
    IPAddress multicastAddress = IPAddress.Parse(address);
    foreach (IPAddress localAddress in localAddresses)
    {
        var udpClient = new UdpClient(AddressFamily.InterNetwork);
        udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        udpClient.Client.Bind(new IPEndPoint(localAddress, port));
        udpClient.JoinMulticastGroup(multicastAddress, localAddress);
        udpClient.BeginReceive(OnReceiveSink,
                               new object[]
                                   {
                                       udpClient, new IPEndPoint(localAddress, ((IPEndPoint) udpClient.Client.LocalEndPoint).Port)
                                   });
    }
}

И асинхронный метод:

private static void OnReceiveSink(IAsyncResult result)
{
    IPEndPoint ep = null;
    var args = (object[]) result.AsyncState;
    var session = (UdpClient) args[0];
    var local = (IPEndPoint) args[1];

    byte[] buffer = session.EndReceive(result, ref ep);
    //Do what you want here with the data of the buffer

    Console.WriteLine("Message received from " + ep + " to " + local);

    //We make the next call to the begin receive
    session.BeginReceive(OnReceiveSink, args);
}

Надеюсь, это поможет ;)

person J4N    schedule 08.03.2013

Вам необходимо присоединиться к многоадресной группе через все доступные интерфейсы. По умолчанию исходящее сообщение IGMP JOIN будет маршрутизироваться в соответствии с таблицами маршрутизации одноадресной рассылки, которые будут отправлять его по «самому дешевому» маршруту, используя любую сетевую карту, которая обращается к этому маршруту. Если ваша группа многоадресной рассылки может быть получена более чем одним из этих маршрутов, вам необходимо выполнить итерацию.

person user207421    schedule 07.03.2013
comment
Я не уверен, что понимаю, mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);, который делается для всех UdpClient, делает это, нет? Если нет, то как мне это сделать? - person J4N; 07.03.2013
comment
Вы должны перебирать IP-адреса локальных интерфейсов и присоединяться через каждый из них. - person user207421; 08.03.2013
comment
Я думаю, вы не видели мою вторую цитату кода, это именно то, что я делаю. - person J4N; 08.03.2013
comment
@ J4N Отстань. Это именно то, что вы сейчас делаете, в коде вашего ответа, который был опубликован через несколько часов после того, как я опубликовал это. Ключевым моментом является перебор локальных IP-адресов и использование формы JoinMulticastGroup(), которая принимает локальный адрес. Вы не были и не делаете этого в коде в своем вопросе, на что я отвечаю. - person user207421; 09.03.2013
comment
Проверьте мое второе редактирование кода, я уже повторяю все свои локальные IP-адреса. Разница в том, что сейчас я использую асинхронные методы. - person J4N; 09.03.2013
comment
@J4N Нет, разница в том, что теперь вы делаете то, что я сказал вам делать в первую очередь, присоединяясь к группе многоадресной рассылки через все адреса всех локальных интерфейсов, используя форму JoinMulticastGroup() Я упоминал выше, что в качестве аргумента принимается локальный адрес. Другого способа сделать это нет. Ваш второй фрагмент кода этого не делает, и поэтому он уже не делает то, что я сказал. Использование асинхронного API не имеет к этому никакого отношения. Вы не можете всерьез полагать, что многоадресная многоадресная рассылка не работала, пока не появился Async API. Я делал это десять лет назад. - person user207421; 09.03.2013