C# UdpSocket начинает получать после отправки и прекращает получать после нескольких пакетов

Я пытаюсь получить данные через UdpSocket с адреса многоадресной рассылки. Сокет не получает данные, пока я не отправил данные через сокет. После отправки я могу получить несколько пакетов, а затем мне нужно отправить еще раз, прежде чем я смогу получить больше пакетов. Тем временем отправленные с других хостов пакеты теряются. Я думаю, это не проблема брандмауэра, как здесь: C# UDP Socket не получает данные до тех пор, пока данные не отправлены, потому что whireshark получает все пакеты. Может кто-нибудь объяснить мне это поведение?

class Program
{
    private static UdpClient _mdnsSocket;
    private static IPEndPoint _mdnsGroup;
    private static IPEndPoint _localEp;

    static void Main(string[] args)
    {
        var interfaces = NetworkInterface.GetAllNetworkInterfaces()
                                         .Where(i => i.OperationalStatus == OperationalStatus.Up)
                                         .ToArray();

        for (int i = 0; i < interfaces.Length; ++i)
        {
            var interf = interfaces[i];
            Console.WriteLine("{0}) Name: {1}", i, interf.Name);
        }
        Console.WriteLine();

        do
        {
            int i;
            Console.Write("Interface: ");
            var line = Console.ReadLine();
            if (int.TryParse(line, out i) && i < interfaces.Length)
            {
                var addr = interfaces[i].GetIPProperties()
                                        .UnicastAddresses.FirstOrDefault(a => a.Address.AddressFamily == AddressFamily.InterNetwork);
                if (addr != null)
                {
                    _localEp = new IPEndPoint(addr.Address, 5353);
                    Console.WriteLine("Choosen IP: {0}", _localEp);
                }
            }
        } while (_localEp == null);


        _mdnsGroup = new IPEndPoint(IPAddress.Parse("224.0.0.251"), 5353);
        _mdnsSocket = new UdpClient();
        _mdnsSocket.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        _mdnsSocket.ExclusiveAddressUse = false;
        _mdnsSocket.Client.Bind(_localEp);
        _mdnsSocket.JoinMulticastGroup(_mdnsGroup.Address, _localEp.Address);
        BeginReceive();

        Console.WriteLine("1 to switch to multicast mode (default)");
        Console.WriteLine("2 to switch to unicast mode");
        Console.WriteLine("s for sending a message");
        Console.WriteLine("ESC for exit");

        ConsoleKey key;
        IPEndPoint ip = _mdnsGroup;
        IPEndPoint unicastip = null;
        var mode = "multicast";

        do
        {
            Console.Write("1/2/s/ESC: ");
            key = Console.ReadKey().Key;
            Console.WriteLine();

            switch (key)
            {
                case ConsoleKey.D1:
                    ip = _mdnsGroup;
                    Console.WriteLine("Switched to multicast mode");
                    mode = "multicast";
                    break;

                case ConsoleKey.D2:
                    Console.Write("Enter new IP (leave empty to use {0}):", unicastip);
                    var input = Console.ReadLine();
                    if (string.IsNullOrEmpty(input))
                    {
                        if (unicastip == null)
                        {
                            Console.WriteLine("error: no last ip!");
                            break;
                        }
                        ip = unicastip;
                        Console.WriteLine("Switched to unicast mode");
                        mode = "unicast";
                    }
                    else
                    {
                        unicastip = new IPEndPoint(IPAddress.Parse(input), 5353);
                        ip = unicastip;
                        Console.WriteLine("Switched to unicast mode");
                        mode = "unicast";
                    }
                    break;

                case ConsoleKey.S:
                    var msg = string.Format("Hello from PC via {0}", mode);
                    var bytes = Encoding.ASCII.GetBytes(msg);

                    Console.WriteLine("Sending to {0}", ip);
                    _mdnsSocket.Send(bytes, bytes.Length, ip);
                    break;
            }
        } while (key != ConsoleKey.Escape);
        _mdnsSocket.Close();
    }

    private static void BeginReceive()
    {
        Console.WriteLine("BeginReceive");
        _mdnsSocket.BeginReceive(ReceiveCallback, _mdnsSocket);
    }

    private static void ReceiveCallback(IAsyncResult ar)
    {            
        try
        {
            var ep = new IPEndPoint(IPAddress.Any, _mdnsGroup.Port);
            var data = _mdnsSocket.EndReceive(ar, ref ep);

            var message = Encoding.ASCII.GetString(data);
            Console.WriteLine(message);
        }
        finally
        {
            BeginReceive();
        }
    }
}

1 ответ

Решение

В конце концов, похоже, это проблема с брандмауэром. Когда я явно разрешаю входящий UPD через порт 5353, он работает (почему вообще не работает весь входящий трафик UPD для соответствующей программы). Теперь я объясню поведение, описанное в вопросе, с помощью так называемого механизма пробивки отверстий. Пожалуйста, поправьте меня, когда я ошибаюсь.

Здесь та же проблема, но брандмауэр не исправил ее. Я использую Windows 10 PRO.

Я обнаружил еще один нежелательный эффект: широковещательные сообщения не принимаются, а прямые IP-сообщения принимаются.

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

Закрытие сокета и повторный запуск также устраняет проблему, но работает медленнее и заставляет GC работать.

Я использую один и тот же класс UDP в приложениях Unity (Mono) и Visual Studio, но этот "эффект" возникает только в приложениях Unity. Это происходит только с сокетами IPV4, сокеты IPV6, кажется, никогда не перестают слушать.

Я уверен, что это своего рода функция "Прекратить использование IPV4-трансляции" в новых ОС, потому что Windows 7 работает нормально.

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

Другие вопросы по тегам