Erlang Udp 服务器丢弃大量数据包

Posted

技术标签:

【中文标题】Erlang Udp 服务器丢弃大量数据包【英文标题】:Erlang Udp server dropping lots of packets 【发布时间】:2015-12-02 04:30:18 【问题描述】:

我一直在测试运行 udp 服务器的各种语言/方法,看看哪种语言/方法的性能最高。到目前为止,我已经测试了 .NET、NodeJs 和 Erlang。我发现此代码片段存在问题,丢弃了我发送的 50% 以上的数据包,而 node 和 .net 大约为 4%。场景是我尽快发送1000 20 byte messages 并将递增的数字打印到屏幕上。 Erlang 只得到了大约 400 个。你能建议我做些什么来改善这个结果吗?

-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;

        


    

【问题讨论】:

我有你的代码,这行io:format("~p~n",[N]), 似乎永远不会执行。这在 erlang shell 中没有输出。你能告诉我是什么原因吗? 你给它发了一个数据包吗?该行仅在收到数据包时打印。 【参考方案1】:

与保证传送的 TCP 不同,UDP 数据包可能会被简单地丢弃。通常这将是溢出的结果。在任何有生产者/消费者关系的地方,您都需要关注生产者超过消费者时会发生什么。使用 TCP 流控制会对发送方施加背压,而使用 UDP 则不存在这种背压。

当您在服务器端打开一个 UDP 套接字时,它将分配一定数量的内存来接收来自网络的数据。如果您的客户端从套接字读取数据的速度不如网络数据到达的速度快,您将遇到缓冲区溢出,新的 UDP 数据包将覆盖以前接收但尚未读取的 UDP 数据包。

您可以使用 rcvbuf 和 sndbuf 选项增加套接字的接收(和传输)缓冲区的大小。但是,您可能必须首先增加内核限制(例如sysctl -w net.core.rmem_max)。

要查看您是否因内核缓冲区溢出而丢失 UDP 数据包,请使用 netstat -su 检查 UDP 统计信息(查看“数据包接收错误”)。

【讨论】:

谢谢,我了解 udp 的工作原理,我正在寻求有关 erlang 代码片段的帮助。 erlang 代码丢失了 70% 的数据包,而 .net/mono/nodjs/python 版本仅丢失了 ~5-10%。 了解它们丢失的位置很重要。您的 Erlang 服务器进程从套接字读取的速度不够快吗?我已经解释了如何诊断以及如何为此优化代码。 好的,我很感激。在我重读你的答案后,我明白你现在在说什么。我已将 net.core.rmem_max 设置为 8MB,并将套接字 recvbuf 设置为 4194304,现在成功率达到了 99.8%。谢谢!我有兴趣了解的一件事是为什么默认套接字设置会如此之低而无法丢弃所有这些数据包,而 python、mono 和 node 都很好【参考方案2】:

我相信有些数据包会丢失,因为在发送时没有人主动期待它们。 您可以通过以下方式修改您的 Erlang 代码,进一步减少接收到的数据包数量:

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

其中一种解决方案可能是使用active, true 选项,这将确保数据包将被定向到侦听进程的邮箱。

顺便说一句,在我的机器上,原始代码可以达到~99%的数据包接收率,但是加上延迟后,Erlang部分只能接收到几十个数据包。

【讨论】:

Active true 仅略微增加了约 15% 的成功率。我应该补充一点,我的代码片段将环回显示为服务器,但实际上服务器是一个 ec2 ubuntu 14.04 实例,而我的客户端在我的家庭网络上。 您是否也在 AWS 上测试您的 .NET 和 Node.js 服务器? 是的,对 node 使用相同的环境,对 .net 使用不同的 windows 实例。我打算在 linux 上尝试使用 .NET Core,但 System.Sockets 命名空间还没有完成。 我刚刚在同一个 linux 主机上使用 mono 重新编译了 .net 服务器,成功率达到了 96.3%。 @Wjdavis5 我有你的代码,这一行 io:format("~p~n",[N]),似乎永远不会执行。这在 erlang shell 中没有输出。你能告诉我是什么原因吗? ——

以上是关于Erlang Udp 服务器丢弃大量数据包的主要内容,如果未能解决你的问题,请参考以下文章

使用 esp32 接入点流式传输 UDP 数据包会导致大量数据包丢失

erlang udp服务器无法接收接受数据包

Windows,UDP数据包静默丢弃

丢弃大于 1500 字节的 UDP 数据包

udp 隧道中的数据包丢弃和拆分

C#多播UDP数据包丢弃,同时存储对象