“怎么可能?”;“数据包”;Erlang中的socket选项能如此加速tcp传输吗?

“怎么可能?”;“数据包”;Erlang中的socket选项能如此加速tcp传输吗?,tcp,erlang,ip,packet,transmission,Tcp,Erlang,Ip,Packet,Transmission,使用{packet,4}通过本地主机上的两个不同端口传输1G数据只需8秒钟,而使用{packet,raw}无法在30秒内完成相同的任务。我知道如果使用后一种方法,数据将以数以万计的小块形式到达(在archlinux上,大小为1460字节)。我已经学习了TCP/IP协议的一些方面,并且已经思考这个问题好几天了,但仍然无法找出确切的区别。真诚期待一些自下而上的解释 -module(test). -export([main/1]). -define(SOCKOPT, [binary,{active

使用{packet,4}通过本地主机上的两个不同端口传输1G数据只需8秒钟,而使用{packet,raw}无法在30秒内完成相同的任务。我知道如果使用后一种方法,数据将以数以万计的小块形式到达(在archlinux上,大小为1460字节)。我已经学习了TCP/IP协议的一些方面,并且已经思考这个问题好几天了,但仍然无法找出确切的区别。真诚期待一些自下而上的解释

-module(test).

-export([main/1]).

-define(SOCKOPT, [binary,{active,true},{packet,4}]).

main(_) ->
    {ok, LSock} = gen_tcp:listen(6677, ?SOCKOPT),
    spawn(fun() -> send() end),
    recv(LSock).

recv(LSock) ->
    {ok, Sock} = gen_tcp:accept(LSock),
    inet:setopts(Sock, ?SOCKOPT),
    loop(Sock).

loop(Sock) ->
    receive
        {tcp, Sock, Data} ->
            io:fwrite("~p~n",[bit_size(Data)]),
            loop(Sock);
        {tcp_closed, Sock} -> ok
    end.

send() ->
    timer:sleep(500),
    {ok, Sock}=gen_tcp:connect("localhost", 6677, ?SOCKOPT),
    gen_tcp:send(Sock, binary:copy(<<"1">>, 1073741824)),
    gen_tcp:close(Sock).
-模块(测试)。
-导出([main/1])。
-定义(SOCKOPT,[binary,{active,true},{packet,4}])。
main(u41;)->
{ok,LSock}=gen_tcp:listen(6677,?SOCKOPT),
繁殖(fun()->send()结束),
recv(LSock)。
recv(LSock)->
{ok,Sock}=gen_tcp:accept(LSock),
inet:setopts(Sock,?SOCKOPT),
环(袜子)。
循环(袜子)->
接收
{tcp,Sock,Data}->
io:fwrite(“~p~n”,位大小(数据)],
环(袜子);
{tcp_关闭,Sock}->确定
结束。
发送()->
定时器:睡眠(500),
{ok,Sock}=gen_tcp:connect(“localhost”,6677,?SOCKOPT),
gen_tcp:send(Sock,binary:copy(,1073741824)),
gen_tcp:关闭(Sock)。
$time escript test.erl
8589934592
实数0m8.919s 用户0m6.643s 系统0m2.257s

当您使用{packet时,4}erlang首先读取4个字节来获取数据长度,分配一个缓冲区来保存数据,并在获取每个tcp数据包后将数据读入缓冲区。然后它将缓冲区作为一个数据包发送到您的进程。这一切都发生在内置读取代码中,这相当快


当您使用{packet,raw}erlang在接收到每个tcp数据包后向您的进程发送一条消息,因此对于每个tcp数据包,它会做更多的事情。

当接收到小块数据时,接收器端的内核缓冲区会很快填满。它将减少发送方端的拥塞窗口大小,迫使发送方以较低的速率推送数据。

试试看

-define(SOCKOPT, [binary,{active,true},{recbuf, 16#FFFFFF}, {sndbuf, 16#1FFFFFF}])

我是一个Erlang文盲,但是你能给我解释一下
{packet,4}
的意思吗?那么我也许可以回答你的问题了;如果您通过TCP发送一大块数据,它将被拆分,您必须知道原始数据块有多大,才能知道如何重新组装它。数据包模式为您提供服务。这就是为什么在包模式下只发送一条消息:在幕后,Erlang抓取所有相关的包,并在发送消息之前将它们缝合在一起。您不必调用receive数千次,这也许是速度更快的原因,但如果您发布代码,则更容易了解到底发生了什么。首先,确保您的两个测试尽可能相似。一个是对stdout进行735400格式的写入,另一个是单次写入。删除io:fwrite/2并再次运行测试。第二,escript默认为解释模式,这可能会导致结果极度扭曲。添加a模式(编译)。谢谢!你刚刚解决了所有的问题,真是太棒了。谢谢分享你的想法。但是我认为使用{packet,4}只会简化接收方的代码,而不会加快传输速度,因为它不会改变发送缓冲区和接收缓冲区。使用{packet,4}意味着您的接收方代码不会每1.5KB(最大tcp包大小)调用一次,只会每1GB调用一次。在我看来,使用{packet,4}只会扩展用户级缓冲区,这也可以减少调用,但当我们发送如此大的数据时,传输速率可能与内核级别的sndbuf和recbuf更相关。事实上,当我用{sndbuf,4194304},{recbuf,4194304}替换{packet,4}时,我得到了更快的结果。有没有办法告诉它使用大/小的endian?没有。只有破解Erlang虚拟机或者自己用
=gen_tcp:recv(Socket,4),Data=gen_tcp:recv(Socket,Len)
和使用被动接收。
-define(SOCKOPT, [binary,{active,true},{recbuf, 16#FFFFFF}, {sndbuf, 16#1FFFFFF}])