2015-09-06 2 views
3

Я тестировал различные языки/методы для запуска udp-сервера, чтобы увидеть, какой из них будет самым результативным. До сих пор я тестировал .NET, NodeJs, а затем Erlang. Я вижу проблему с этим снижением кода, отбрасывающим более 50% пакетов, которые я отправляю, тогда как узлы и .net составляли ~ 4%. Сценарий заключается в том, что я отправляю 1000 20 byte messages как можно быстрее и печатаю инкрементный номер на экран. Эрланг получает только 400 из них. Не могли бы вы предложить что-нибудь, что я мог бы сделать, чтобы улучшить этот результат?Сервер Erlang Udp, отбрасывающий множество пакетов

-module(udp). 
-export([start/0]). 

start() -> 
    spawn(fun() -> server(41235) end). 

server(Port) -> 
    {ok, Socket} = gen_udp:open(Port, [binary, {active, false}]), 
    io:format("server opened socket:~p~n",[Socket]), 
    loop(Socket,0). 

loop(Socket,N) -> 
    inet:setopts(Socket, [{active, once}]), 
    receive 
     {udp, Socket, Host, Port, Bin} -> 
      io:format("~p~n",[N]), 
      loop(Socket,N+1) 
    end. 

Я бы не удивился, если полностью задержу этот код erlang. У меня было действительно трудное время, обертывающее мою голову вокруг некоторых концепций.

Мой сервер с благодарностью заимствованы из: http://erlycoder.com/83/erlang-udp-socket-usage-example-with-gen

Вот мой клиент в случае, если вы заинтересованы:

namespace LocalUdpClient 
{ 
    class Program 
    { 
     private static long _sentCount = 1; 
     private static CustomQueue _queue; 
     private static bool _continue = true; 
     static void Main(string[] args) 
     { 
      _queue = new CustomQueue(); 
      _queue.ItemRemovedEventHandler += QueueOnItemRemovedEventHandler; 
      PopulateQueue(); 
      var con = new System.Net.Sockets.UdpClient(); 
      con.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 41235)); 

      while (_continue) 
      { 
       byte[] bits = null; 
       if (_queue.TryDequeue(out bits)) 
       { 
        con.SendAsync(bits, bits.Length); 
        Interlocked.Increment(ref _sentCount); 
        Console.Clear(); 
        Console.Write(Interlocked.Read(ref _sentCount)); 
       } 

      } 

     } 

     private static void QueueOnItemRemovedEventHandler(object sender, EventArgs eventArgs) 
     { 
      var queue = sender as CustomQueue; 
      if (queue.Count <= 0) 
      { 
       Task.Run(() => PopulateQueue()).Wait(); 
      } 
     } 

     public static void PopulateQueue() 
     { 
      Console.ReadLine(); 
      RandomNumberGenerator rand2 = new RNGCryptoServiceProvider(); 
      Parallel.For(0, 1000, p => 
      { 
       if (_queue.Count >= 1000) return; 
       byte[] bytes = new byte[20]; 
       rand2.GetBytes(bytes); 
       _queue.Enqueue(bytes); 
      }); 



     } 

    public class CustomQueue : ConcurrentQueue<byte[]> 
    { 
     public event EventHandler ItemRemovedEventHandler; 

     new public bool TryDequeue(out byte[] item) 
     { 
      var x = base.TryDequeue(out item); 
      if (ItemRemovedEventHandler != null) 
      { 
       ItemRemovedEventHandler(this,new EventArgs()); 
      } 
      return x; 

     } 


    } 
} 
+0

У меня ром ваш код, эта строка 'io: format (" ~ p ~ n ", [N]),' кажется, никогда не вызывается. это не выход в оболочке erlang. Не могли бы вы рассказать мне, в чем причина? – BlackMamba

+0

Вы отправили ему пакет? Эта строка печатает только при получении пакета. – Wjdavis5

ответ

2

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

Когда вы открываете сокет UDP со стороны сервера, он будет иметь определенный объем памяти, выделенный для приема данных из сети. Если ваш клиент не читает из сокета так быстро, как данные поступают из сети, вы получите переполнение буфера, новые UDP-пакеты будут перезаписывать ранее полученные, но еще не прочитанные UDP-пакеты.

Вы можете увеличить размер буфера приема (и передачи) для сокета с параметрами rcvbuf и sndbuf. Возможно, вам придется сначала увеличить пределы ядра (например, sysctl -w net.core.rmem_max).

Чтобы узнать, проигрываете ли вы UDP-пакеты для переполнения буфера ядра, проверьте статистику UDP с помощью netstat -su (посмотрите на «ошибки приема пакетов»).

+0

Спасибо, я понимаю, как работает udp, я ищу помощь по snip-файлу erlang. Код erlang теряет 70% пакетов, а версия .net/mono/nodjs/python только теряет ~ 5-10%. – Wjdavis5

+0

Важно понимать, где они теряются. Проблема в том, что ваш серверный процесс Erlang не читает из сокета достаточно быстро? Я объяснил, как диагностировать это и как оптимизировать код для этого. –

+0

Хорошо, я могу это оценить. После того, как я перечитаю ваш ответ, я вижу, что вы сейчас говорите. Я установил net.core.rmem_max на 8 МБ и установил сокет recvbuf в 4194304 и теперь получаю 99,8% успешных ставок. Спасибо! Одна вещь, которую мне было бы интересно понять, - это то, что настройки сокета по умолчанию были бы настолько низкими, чтобы удалить все эти пакеты, тогда как python, mono и node были в порядке – Wjdavis5

0

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

 io:format("~p~n",[N]), 
     receive after 10 -> ok end, 
     loop(Socket,N+1) 

Одним из решений может быть с помощью {active, true} вариант, который будет гарантировать, что пакеты вместо будут направлены в процессе прослушивания почтовый ящик ,

В качестве побочного примечания на моей машине исходный код мог достичь скорости приема пакетов 99%, но после добавления задержки часть Erlang могла принимать только несколько десятков пакетов.

+0

Активный true только увеличил успех примерно на 15%. Я должен добавить, что мой snip кода показывает loopback как сервер, но на самом деле сервер является экземпляром ec2 ubuntu 14.04, а мой клиент находится в моей домашней сети. – Wjdavis5

+0

Вы тестировали свои серверы .NET и Node.js на AWS? – rpozarickij

+0

Да, используя ту же среду для узла и другой экземпляр Windows для .net. Я собирался попробовать использовать .NET Core на linux, но пространство имен System.Sockets еще не закончено. – Wjdavis5

Смежные вопросы