Socket.Send киоски для ~ 600 мс каждый 55-й звонок - PullRequest
0 голосов
/ 24 февраля 2019

У меня есть приложение C # / WPF, которое отправляет сообщение по UDP 30 раз в секунду.Сообщения отправляются из свободно работающего потока и отправляются на фиксированный IP-адрес.Каждое сообщение разбивается на 1-6 дейтаграмм в зависимости от размера сообщения, поэтому я посылаю от 30 до 180 дейтаграмм в секунду с общей пропускной способностью до ~ 250 кбит / с.Я преследовал проблему, когда скорость отправки из моего приложения иногда резко замедляется.Приложение является многопоточным, и в дополнение к потоку пользовательского интерфейса у меня есть:

  • Свободно работающий фоновый поток, который предоставляет данные для отправки по сети на основе собственной внутренней синхронизации.Это добавляет, создает дейтаграммы сообщений и добавляет их в ConcurrentQueue вместе с адресом назначения
  • Фоновый поток отправителя, который читает ConcurrentQueue и отправляет дейтаграммы на требуемый адрес через Socket.SendTo* 1009.*

Для справки, здесь приведена инициализация сокета:

try
{
    _Socket?.Close();
    _Socket = null;
    _Socket = new Socket(SocketType.Dgram, ProtocolType.Udp);
    _Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    _Socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastLoopback, true);
    _Socket.EnableBroadcast = true;

    _Socket.Bind(_LocalEP);
}
catch (SocketException e)
{
    utils.error.SocketExceptionMessage(e, _LocalEP.Address, (UInt16)_LocalEP.Port);
    Stop();
}

После долгих поисков теней я выделил поведение для вызова Socket.Sendto, установив для измерения поток отправителя.время, которое занимает вызов:

private void SendThreadF()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    List<long> loopDeltaTs = new List<long>();
    long lastLoopTime=0;

    while (CanSend)
    {
        DGMessage m;
        while (SendQueue.TryDequeue(out m))
        {
            if (CanSend)
            {
                lastLoopTime = sw.ElapsedMilliseconds;
                _Socket.SendTo(m.Datagram, m.Length, SocketFlags.None, m.Destination);
                loopDeltaTs.Add(sw.ElapsedMilliseconds - lastLoopTime);

            }

        }
        Thread.Sleep(10);
    }
    Trace.WriteLine("Send Thread is Exiting!");
}

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

  • Почти все вызовы занимают 0-1 миллисекунды, как и ожидалось.
  • Каждый 54-й или 55-й вызов занимает 590-598 миллисекунд.
  • Этот шаблон, по-видимому, надежно повторяется, пока выполняется поток (десяткитысячи образцов)
  • Относительное время с регулярными перерывами с интервалами ~ 600 мс точно соответствует тому, что я вижу в Wireshark

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

Вот странная вещь: я перезагрузил компьютер, и проблема прекратилась.Это говорит о том, что это может вообще не иметь никакого отношения к моему приложению, но это все равно будет представлять проблему поддержки пользователей.К сожалению, я в растерянности, как решить эту проблему дальше.

  • Есть ли какая-либо причина, по которой это может быть вызвано конфигурацией сокета (хотя я видел эту проблему раньше, когда использовал UdpClient вместо сокетов)?
  • Есть ли какой-либо известный способчто другое приложение может получить доступ к тому же порту таким образом, что доступ моего приложения может быть заблокирован?(В Wireshark, FWIW нет соответствующего трафика)
  • Есть ли что-нибудь еще, что я могу сделать, чтобы обойти или диагностировать эту проблему, когда она возникает?
...