Erlang网络编程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gohuge/article/details/79878407

一、Socket消息模式
Erlang的socket有3种消息接收模式:active、passive和active once;
可以在gen_tcp:connect/3或gen_tcp:listen/2里设置{active, true | false |once}来实现,也可以用inet:setopts/2来动态设置。

这3种模式的区别是:
1. active(主动消息接收):非阻塞。当数据到达时系统会向控制进程发送{tcp, Socket, Data}消息。控制进程无法控制消息流;
2. passive(被动消息接收):阻塞。控制进程必须主动调用recv()来接收消息。可以控制消息流;
3. active once(混合消息接收):半阻塞。这种模式是主动的,但仅针对一个消息在控制进程收到一个消息后,必须显式调用inet:setopts(Socket, [{active, once}])来重新接收下一个消息。可以进行流量控制。这种模式相对于被动模式来说,优点是可以同时等待多个socket的数据。

二、数据封包处理
1、{active, false} 方式通过 gen_tcp:recv(Socket, Length) -> {ok, Data} | {error, Reason} 来接收。
2、{active, true} 方式以消息形式{tcp, Socket, Data} | {tcp_closed, Socket} 主动投递给线程。

第一种方式:
gen_tcp:recv/2,3,如果封包的类型是{packet, raw}或者{packet,0},就需要显式的指定长度,否则封包的长度是对端决定的,长度只能设置为0。如果长度Length设置为0,gen_tcp:recv/2,3会取出Socket接收缓冲区所有的数据
第二种方式:
缓存区有多少数据,都会全部以消息{tcp, Socket, Data} 投递给线程。

3、socket的选项里面的{packet,0}和{packet,raw}的区别
{packet,2} erlang处理2字节大端包头;
{packet,4} erlang处理4字节大端包头;
{packet,0} erlang不负责拆包,用户自己处理;
{packet,raw} erlang不负责拆包,用户自己处理,t可以处理icmp之类的特殊包。

4、粘包处理
当client在极短的时间内发送多个包给server,这时server在接收数据的时候可能发生连包问题,就一次性接收这几个包的数据,导致数据都粘连在一起。
自己处理粘包的时候,使用{active, N}(还没有到被动模式)和{active, true}选项的时候,在handle_info({tcp,Socket,Data}里面需要好好处理下粘包,

三、WS帧数据
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
byte1 
(1)Fin代表数据是否结束,WebSocket会把较大的数据分成片发送,最后一片数据的Fin为1,代表数据片完结 
(2)RSV1-RSV3是保留为,一般为0 
(3)最后4bit代表Opcode,OpCode用来指示数据帧的类型。WebSocket的帧分为两大类型,数据帧和控制帧。 
0x0 代表连续帧,也就因为这该帧数据是分片数据中的一片,并不是完整数据 
0x1 代表数据是文本内容 
0x2 代表数据时二进制流 
0x3-0x7 保留给日后的非控制帧使用 
0x8 代表该数据时一个关闭命令通知(下面会解释关闭) 
0x9 代表Ping帧(同样下面会解释Ping) 
0xA 代表Pong帧 
0xB-0xF 保留给日后的控制帧使用
byte2 
(1)Mask代表发来的帧中的数据,是否经过掩码处理,1为true,0为false,一般在客户端发给服务器端的数据中,该值都是1,也就是经过掩码处理,服务器发往客户端的不用掩码。(注意,所谓的客户端,服务端是相对的,接收WebSocket连接的那一端,也就是上面提到的回发加密处理的那一端是服务器端。这也解释了,为什么我们要遵循WebSocket标准来进行握手,否则客户端怎么知道自己发的数据得要掩码处理呢) 
(2)后面7位代表数据帧的数据长度或者是一个长度指示。我自己理解为是一个长度预判。当数据长度不超过125字节时,该值就是实际的数据长度,当长度在126~65535时,该值为固定的126,超过65535,该值固定为127
byte3~byte4 
当Payload len = 126时,保存的是该帧数据的16位真实长度
byte3~byte10 
当Payload len = 127时,保存的是该帧数据64位的真实长度
注意,如果长度不超过125,那么byte3~byte10就不代表数据长度了,也就是说不会预留给数据长度用,而是给后续的帧头信息使用,后续帧头的字节信息左移
byte11~byte14 
这4个字节代表掩码值,用客户端指定,每个包都不一样,只有经过掩码值的解码处理,才能获得正确的数据
由此可以看到,WebSocket的消息封包,服务器端至少需要2个字节,客户端至少6个字节。

猜你喜欢

转载自blog.csdn.net/gohuge/article/details/79878407