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