Sockets 在Erlang活动模式下,使用ASCII数据包大小标头从套接字接收数据包

Sockets 在Erlang活动模式下,使用ASCII数据包大小标头从套接字接收数据包,sockets,tcp,erlang,Sockets,Tcp,Erlang,我有Erlang SSL TCP套接字,它与另一方有永久TCP连接, 我们使用一个类似于ISO8583协议的协议,四个第一字节是一个ASCII编码的数据包大小。 基于Erlang inet文档(),它只支持包大小的无符号整数 头长度可以是一个、两个或四个字节,并且包含一个大端字节顺序的无符号整数。 现在我使用gen_server handle_info,当我收到一个数据包时,我首先读取四个字节,然后将其与二进制大小进行比较,如果二进制大小很小,我什么也不做,将收到的二进制文件放入LastBin并

我有Erlang SSL TCP套接字,它与另一方有永久TCP连接, 我们使用一个类似于ISO8583协议的协议,四个第一字节是一个ASCII编码的数据包大小。 基于Erlang inet文档(),它只支持包大小的无符号整数

头长度可以是一个、两个或四个字节,并且包含一个大端字节顺序的无符号整数。

现在我使用gen_server handle_info,当我收到一个数据包时,我首先读取四个字节,然后将其与二进制大小进行比较,如果二进制大小很小,我什么也不做,将收到的二进制文件放入LastBin并等待数据包的其余部分,如果数据包中有多个消息,我会多次调用read_iso数据包,如果我所做的是这样的话,请提供一个简短的示例:

handle_info({ConnType, _Socket, Bin}, {BSSl, BProtocol, Psize, OSocket, LastBin})
    when ConnType =:= ssl; ConnType =:= tcp ->
    logger:debug("mp_back:Got response from backend~w", [Bin]),
          read_iso_packet(Bin, {BSSl, BProtocol, Psize, OSocket, LastBin})
    end.
read_iso_packet(Bin, {BSSl, BProtocol, Psize, Socket, LastBin})
    when size(<<LastBin/binary, Bin/binary>>) < 5 ->
    {noreply, {BSSl, BProtocol, Psize, Socket, <<LastBin/binary, Bin/binary>>}};
read_iso_packet(Bin, {BSSl, BProtocol, Psize, Socket, LastBin})
    when size(<<LastBin/binary, Bin/binary>>) > 4 ->
    Packe_Size = get_packet_size(binary:part(<<LastBin/binary, Bin/binary>>, 0, 4)),
    logger:debug("mp_back:packet_size==~w", [Packe_Size]),
    logger:debug("mp_back:bin_size==~w", [size(Bin)]),
    read_iso_packet(Packe_Size + 4 - size(<<Bin/binary, LastBin/binary>>),
                    Packe_Size,
                    <<LastBin/binary, Bin/binary>>,
                    {BSSl, BProtocol, Psize, Socket, LastBin}).

read_iso_packet(0, _Packe_Size, Bin, {BSSl, BProtocol, Psize, Socket, _LastBin}) ->
    do_somthing(server_response, CSocket, Bin),
    {noreply, {BSSl, BProtocol, Psize, Socket, <<>>}};
read_iso_packet(SS, Packe_Size, Bin, {BSSl, BProtocol, Psize, Socket, _LastBin})
    when SS < 0 ->
    do_somthing(server_response, CSocket, [binary:part(Bin, 0, Packe_Size + 4)]),        
    read_iso_packet(binary:part(Bin, Packe_Size + 4, byte_size(Bin) - (Packe_Size + 4)),
                    {BSSl, BProtocol, Psize, Socket, <<>>});
read_iso_packet(SS, _Packe_Size, Bin, {BSSl, BProtocol, Psize, Socket, _LastBin})
    when SS > 0 ->
    logger:debug("mp_back: Small data going to read next~w", [Bin]),
    {noreply, {BSSl, BProtocol, Psize, Socket, Bin}}.

get_packet_size(Bin) ->
    {ok, [A], _} = io_lib:fread("~16u", binary_to_list(binary:part(Bin, 0, 1))),
    {ok, [B], _} = io_lib:fread("~16u", binary_to_list(binary:part(Bin, 1, 1))),
    {ok, [C], _} = io_lib:fread("~16u", binary_to_list(binary:part(Bin, 2, 1))),
    {ok, [D], _} = io_lib:fread("~16u", binary_to_list(binary:part(Bin, 3, 1))),
    A * 1000 + B * 100 + C * 10 + D.
handle_info({ConnType,_Socket,Bin},{BSSl,BProtocol,Psize,OSocket,LastBin})
当ConnType=:=ssl时;ConnType=:=tcp->
记录器:调试(“mp_back:get response from backend~w”,[Bin]),
read_iso_数据包(Bin、{BSSl、BProtocol、Psize、OSocket、LastBin})
结束。
read_iso_数据包(Bin、{BSSl、BProtocol、Psize、Socket、LastBin})
当大小()小于5->
{noreply,{BSSl,BProtocol,Psize,Socket,};
read_iso_数据包(Bin、{BSSl、BProtocol、Psize、Socket、LastBin})
当size()>4->
Packe_Size=get_packet_Size(二进制:部分(,0,4)),
记录器:调试(“mp_back:packet_size==w,[Packe_size]),
记录器:调试(“mp_back:bin_size==w”,[size(bin)],
读取iso数据包(数据包大小+4-Size(),
包装尺寸,
,
{BSSl,BProtocol,Psize,Socket,LastBin})。
读取iso数据包(0,数据包大小,Bin,{BSSl,BProtocol,Psize,Socket,{LastBin})->
执行某些操作(服务器响应、CSocket、Bin),
{noreply,{BSSl,BProtocol,Psize,Socket,};
读取iso数据包(SS、数据包大小、Bin、{BSSl、BProtocol、Psize、套接字、{LastBin})
当SS<0->
do_somthing(服务器响应,CSocket,[二进制:部分(Bin,0,Packe_Size+4)],
读取iso_数据包(二进制:部分(Bin,Packe_大小+4,byte_大小(Bin)-(Packe_大小+4)),
{BSSl,BProtocol,Psize,Socket,});
读取iso数据包(SS、数据包大小、Bin、{BSSl、BProtocol、Psize、套接字、{LastBin})
当SS>0->
记录器:调试(“mp_back:Small data to read next~w”,[Bin]),
{noreply,{BSSl,BProtocol,Psize,Socket,Bin}。
获取数据包大小(Bin)->
{ok[A],}=io_-lib:fread(“~16u”,二进制:部分(Bin,0,1)),
{ok[B],}=io_-lib:fread(“~16u”,二进制:部分(Bin,1,1)),
{好的,[C],}=io_-lib:fread(“~16u”,二进制:部分(Bin,2,1)),
{ok[D],}=io_-lib:fread(“~16u”,二进制:部分(Bin,3,1)),
A*1000+B*100+C*10+D。
我的问题是:

  • 有更好的方法吗?上一次我犯了一个错误,多次阅读了一些消息(我修复了那个bug,但还没有在生产环境中测试我的代码,但在测试中它似乎还可以)。当数据包大小为无符号整数时,Erlang可以为我处理这个问题,但使用ASCII编码的数据包大小不会成功
  • 我尝试使用{active,once}和gen_tcp:recv,但对我来说没有正常工作,这是一种更安全的方法吗
  • gen_服务器:处理_信息是否同步

  • 问题1:您的数据包协议不符合erlang的数据包协议,因此我认为您需要在
    raw
    模式下通过指定
    {packet,raw}
    或等效的
    {packet,0}
    来读取套接字,请参阅

    我不确定您是如何使用
    handle\u info()
    从套接字读取的。您是否将
    {active,true}
    设置为使发送到套接字的数据位于genserver的邮箱中?如果是这样的话,我认为这是行不通的,因为
    {active,true}
    告诉erlang自动从套接字读取
    N
    字节,其中
    N
    由打开套接字时的
    {packet,N}
    指定。在您的情况下,
    N
    将是4。Erlang然后使用这4个字节中包含的整数,我们称之为
    MsLen
    ,从套接字读取
    MsLen
    字节。然后,Erlang将从套接字读取的所有数据块组合成一条完整的消息,并将完整的消息放在genserver的邮箱中。但是,您的
    MsLen
    将是错误的,因为它不是无符号整数,而是ascii编码的整数。因此,我认为您需要在被动模式下打开套接字,
    {active,false}
    ,使用
    gen_tcp:recv()
    读取前四个字节,解码以获得整数长度,然后再次调用
    gen_tcp:recv()
    从套接字读取那么多字节

    或者,您可以指定
    {active,true}
    {packet,raw}
    ,以便发送到套接字的任何数据都将进入genserver的邮箱。在这种情况下,消息将由底层传输机制发送到套接字的任意大小的块组成。因此,您需要使用一个围绕接收块的循环来不断地从邮箱提取消息,直到获得足够的字节来完成一条完整的消息

    问题2:当您在
    active
    模式下打开套接字时,
    {active,true}
    ,erlang会自动从套接字读取
    N
    字节数,其中
    N
    {packet,N}
    中指定,然后,erlang将这些块组合成一个完整的消息,并将消息放在进程邮箱中,该邮箱只能由接收子句读取。调用
    gen\u tcp:recv()
    读取套接字,在这种情况下没有帮助。请参见此处的详细信息:

    指定
    {active,once}
    告诉erlang为一条消息打开带有
    {active,true}
    的套接字,然后套接字切换到
    {active,false}
    或被动模式。在被动模式下,进程需要通过调用
    gen直接从套接字读取数据