企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
1.粘包问题 数据连包问题,这个在client/server的通讯中很常见。就是,当client在极短的时间内发送多个包给server,这时server在接收数据的时候可能发生连包问题,就一次性接收这几个包的数据,导致数据都粘连在一起。 这里先讨论{packet, raw}或者{packet,0}的情况,分别看下{active, Boolean}的两种方式: gen_tcp对socket数据封包的获取有以下2种方式, 1、{active, false} 方式通过 gen_tcp:recv(Socket, Length) -> {ok, Data} | {error, Reason} 来接收。 gen_tcp:recv/2,3,如果封包的类型是{packet, raw}或者{packet,0},就需要显式的指定长度,否则封包的长度是对端决定的,长度只能设置为0。如果长度Length设置为0,gen_tcp:recv/2,3会取出Socket接收缓冲区所有的数据 2、{active, true} 方式以消息形式{tcp, Socket, Data} | {tcp_closed, Socket} 主动投递给线程。 缓存区有多少数据,都会全部以消息{tcp, Socket, Data} 投递给线程。 以上就会导致数据连包问题,那么如何解决呢? {packet, PacketType} 现在再来看下 {packet, PacketType},erlang的解释如下: raw | 0 没有封包,即不管数据包头,而是根据Length参数接收数据。 1 | 2 | 4 表示包头的长度,分别是1,2,4个字节(2,4以大端字节序,无符号表示),当设置了此参数时,接收到数据后将自动剥离对应长度的头部,只保留Body。 asn1 | cdr | sunrm | fcgi |tpkt|line 设置以上参数时,应用程序将保证数据包头部的正确性,但是在gen_tcp:recv/2,3接收到的数据包中并不剥离头部。 http | http_bin 设置以上参数,收到的数据将被erlang:decode_packet/3格式化,在被动模式下将收到{ok, HttpPacket},主动模式下将收到{http, Socket, HttpPacket}. {packet, N} 也就是说,如果packet属性为1,2,4,可以保证server端一次接收的数据包大小。 下面我们以 {packet, 2} 做讨论。 gen_tcp 通信传输的数据将包含两部分:包头+数据。gen_tcp:send/2发送数据时,erlang会计算要发送数据的大小,把大小信息存放到包头中,然后封包发送出去。 所以在接收数据时,要根据包头信息,判断接收数据大小。使用gen_tcp:recv/2,3接收数据时,erlang会自动处理包头,获取封包数据。 {ok, ListenSocket} = gen_tcp:listen(?PORT, [binary,{active,false}]), {ok, ListenSocket2} = gen_tcp:listen(?PORT2, [binary,{active,false},{packet,2}]), {ok,Socket} = gen_tcp:connect({127,0,0,1},?PORT,[binary,{active,false}]), gen_tcp:send(Socket, "1"), gen_tcp:send(Socket, "2"), gen_tcp:send(Socket, "3"), gen_tcp:send(Socket, "4"), gen_tcp:send(Socket, "5"), received message <<"12345">> {ok,Socket} = gen_tcp:connect({127,0,0,1},?PORT2,[binary,{active,false},{packet,2}]), gen_tcp:send(Socket, "1"), gen_tcp:send(Socket, "2"), gen_tcp:send(Socket, "3"), gen_tcp:send(Socket, "4"), gen_tcp:send(Socket, "5"), received message <<"1">> received message <<"2">> received message <<"3">> received message <<"4">> received message <<"5">> 2.大小端问题 字节序分为两类:Big-Endian和Little-Endian,定义如下: a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 其实还有一种网络字节序,为TCP/IP各层协议定义的字节序,为Big-Endian。 packet包头是以大端字节序(big-endian)表示。如果erlang与其他语言,比如C++,就要注意字节序问题了。如果机器的字节序是小端字节序(little-endian),就要做转换。 {packet, 2} :[L1,L0 | Data] {packet, 4} :[L3,L2,L1,L0 | Data]