erlang的Socket参数含义

http://blog.csdn.net/pkutao/article/details/8572216

{ok, Listen} = gen_tcp:listen(?defPort, [binary, {packet, 2},{reuseaddr, true},{active, true}]),
%gen_tcp表用TCP连接
%binary表二进制流方式
%packet,2:表包头长度2字节
%reuseaddr, true:表多个实例可重用同一端口
% {active,true} 创建一个主动套字节(非阻塞)
% {active,false} 创建一个被动套字节(阻塞),如果为false表必须手工处理阻塞,否则阻塞在此处无法收听,当前我无法处理
%{active, once} 创建一个一次性被动套字节(阻塞),只收听一次后堵塞,必须调用inet:setopts(Socket, [{active, once}]),后才可收听下一条
% {active,once} 创建一个主动套字节仅接收一条消息,如想接收下一条必须再次激活(半阻塞)

packet是erlang网络编程中使用频率较高的一个参数,例如:

gen_tcp:listen(Port, [binary, {active, true}, {packet, 2}]) 

表示接收到的包头有两个字节:

receive 
           {tcp, Socket, Binary} ->

接收到的Binary中将不包含2字节的包头,包头会剥离,我们收到的将只是单纯的Body,这极大的方便了我们编程。

packet支持的参数有:

raw | 0

未完成的packeting,即不管数据包头,而是根据langth参数接收数据。

1 | 2 | 4

表示包头的长度,分别是1,2,4个字节(在大端字节序中还包含一个无符号整型),当设置了此参数时,接收到数据后将自动剥离对应长度的头部,只保留Body。

asn1 | cdr | sunrm | fcgi | tpkt | line

设置以上参数时,应用程序将保证数据包头部的正确性,但是在gen_tcp:recv2,3接收到的数据包中并不剥离头部。

http | http_bin

设置以上参数,收到的数据将被erlang:decode_packet/3格式化,在被动模式下将收到{ok, HttpPacket},主动模式下将收到{http, Socket, HttpPacket}.

erlang 解决socket 数据粘包问题

我们知道,erlang实现的网络服务器性能非常高。erlang的高效不在于短短几行代码就能写出一个服务端程序,而在于不用太多代码,也能够写出一个高效的服务端程序。而这一切的背后就是erlang对很多网络操作实现了近乎完美的封装,使得我们受益其中。文章将讨论erlang gen_tcp 数据连包问题及erlang的解决方案。

数据连包问题,这个在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} 来接收。
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} 投递给线程。

以上就会导致数据连包问题,那么如何解决呢?

 {packet, PacketType}

现在再来看下 {packet, PacketType},erlang的解释如下:

{packet, PacketType}(TCP/IP sockets)
Defines the type of packets to use for a socket. The following values are valid:

raw | 0
No packaging is done.

1 | 2 | 4
Packets consist of a header specifying the number of bytes in the packet, followed by that number of bytes. The length of header can be one, two, or four bytes; containing an unsigned integer in big-endian byte order. Each send operation will generate the header, and the header will be stripped off on each receive operation.
In current implementation the 4-byte header is limited to 2Gb.

asn1 | cdr | sunrm | fcgi |tpkt |line
These packet types only have effect on receiving. When sending a packet, it is the responsibility of the application to supply a correct header. On receiving, however, there will be one message sent to the controlling process for each complete packet received, and, similarly, each call to gen_tcp:recv/2,3 returns one complete packet. The header is not stripped off.

The meanings of the packet types are as follows: 
asn1 - ASN.1 BER, 
sunrm - Sun‘s RPC encoding, 
cdr - CORBA (GIOP 1.1), 
fcgi - Fast CGI, 
tpkt - TPKT format [RFC1006], 
line - Line mode, a packet is a line terminated with newline, lines longer than the receive buffer are truncated.

http | http_bin
The Hypertext Transfer Protocol. The packets are returned with the format according to HttpPacket described in erlang:decode_packet/3. A socket in passive mode will return {ok, HttpPacket} from gen_tcp:recv while an active socket will send messages like {http, Socket, HttpPacket}.

httph | httph_bin
These two types are often not needed as the socket will automatically switch from http/http_bin to httph/httph_bin internally after the first line has been read. There might be occasions however when they are useful, such as parsing trailers from chunked encoding.

packet大致意义如下:

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会自动处理包头,获取封包数据。

下面写了个例子来说明,保存为 tcp_test.erl

[plain] view plaincopy

  1. -module(tcp_test).
  2. -export([
  3. start_server/0,
  4. start_client_unpack/0, start_client_packed/0
  5. ]).
  6. -define(PORT, 8888).
  7. -define(PORT2, 8889).
  8. start_server()->
  9. {ok, ListenSocket} = gen_tcp:listen(?PORT, [binary,{active,false}]),
  10. {ok, ListenSocket2} = gen_tcp:listen(?PORT2, [binary,{active,false},{packet,2}]),
  11. spawn(fun() -> accept(ListenSocket) end),
  12. spawn(fun() -> accept(ListenSocket2) end),
  13. receive
  14. _ -> ok
  15. end.
  16. accept(ListenSocket)->
  17. case gen_tcp:accept(ListenSocket) of
  18. {ok, Socket} ->
  19. spawn(fun() -> accept(ListenSocket) end),
  20. loop(Socket);
  21. _ ->
  22. ok
  23. end.
  24. loop(Socket)->
  25. case gen_tcp:recv(Socket,0) of
  26. {ok, Data}->
  27. io:format("received message ~p~n", [Data]),
  28. gen_tcp:send(Socket, "receive successful"),
  29. loop(Socket);
  30. {error, Reason}->
  31. io:format("socket error: ~p~n", [Reason])
  32. end.
  33. start_client_unpack()->
  34. {ok,Socket} = gen_tcp:connect({127,0,0,1},?PORT,[binary,{active,false}]),
  35. gen_tcp:send(Socket, "1"),
  36. gen_tcp:send(Socket, "2"),
  37. gen_tcp:send(Socket, "3"),
  38. gen_tcp:send(Socket, "4"),
  39. gen_tcp:send(Socket, "5"),
  40. sleep(1000).
  41. start_client_packed()->
  42. {ok,Socket} = gen_tcp:connect({127,0,0,1},?PORT2,[binary,{active,false},{packet,2}]),
  43. gen_tcp:send(Socket, "1"),
  44. gen_tcp:send(Socket, "2"),
  45. gen_tcp:send(Socket, "3"),
  46. gen_tcp:send(Socket, "4"),
  47. gen_tcp:send(Socket, "5"),
  48. sleep(1000).
  49. sleep(Count) ->
  50. receive
  51. after Count ->
  52. ok
  53. end.

运行如下:

[plain] view plaincopy

  1. C:\>erlc tcp_test.erl
  2. C:\>erl -s tcp_test start_server
  3. Eshell V5.10.2  (abort with ^G)
  4. 1> tcp_test:start_client_packed().
  5. received message <<"1">>
  6. received message <<"2">>
  7. received message <<"3">>
  8. received message <<"4">>
  9. received message <<"5">>
  10. ok
  11. 2> tcp_test:start_client_unpack().
  12. received message <<"12345">>
  13. ok

字节序

字节序分为两类: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]

如何判断机器的字节序,以C++为例

[cpp] view plaincopy

  1. BOOL IsBigEndian()
  2. {
  3. int a = 0x1234;
  4. char b =  *(char *)&a;  //通过将int强制类型转换成char单字节,通过判断起始存储位置
  5. if( b == 0x12)
  6. {
  7. return TRUE;
  8. }
  9. return FALSE;
  10. }

如何转换字节序,以C++为例

[cpp] view plaincopy

  1. // 32位字数据
  2. #define LittletoBig32(A)   ((( (UINT)(A) & 0xff000000) >> 24) | \
  3. (( (UINT)(A) & 0x00ff0000) >> 8)   | \
  4. (( (UINT)(A) & 0x0000ff00) << 8)   | \
  5. (( (UINT)(A) & 0x000000ff) << 24))
  6. // 16位字数据
  7. #define LittletoBig16(A)   (( ((USHORT)(A) & 0xff00) >> 8)    | \
  8. (( (USHORT)(A) & 0x00ff) << 8))

参考

http://blog.csdn.net/mycwq/article/details/18359007

http://www.erlang.org/doc/man/inet.html#setopts-2

seq_loop(Listen).

listen_socket(Socket,Mode)->
receive 
{tcp,Socket,Bin} ->
%process_req(Bin),
%MsgQueueSize->获得有多少个消息包积压,先使用{active, true},如果判断到接收到的消息包太多,再改成 {active, once}。
{message_queue_len, MsgQueueSize} = erlang:process_info(self(),message_queue_len),
io:format("Server received binary = ~p~n",[Bin]),
io:format("Queue size is ~p~n", [MsgQueueSize]),
if
(MsgQueueSize > 500) and (Mode =:= active_true) ->
inet:setopts(Socket, [{active, once}]),
%再次调用收听,开始收听下一条信息 
listen_socket(Socket, active_once);
(MsgQueueSize < 10) and (Mode =:= active_once) ->
inet:setopts(Socket, [{active, true}]),   
listen_socket(Socket, active_true)
true->
io:format("Queue size is ~p~n", [MsgQueueSize]),   
listen_socket(Socket, Mode)
end;
{tcp_closed,Socket} ->
io:format("有人下线 =>~n"),
io:format("Client socket closed ~n")
end.

时间: 2024-10-14 09:09:32

erlang的Socket参数含义的相关文章

Mysqldump参数大全 这 些参数 不同于 mysql 的那些参数(下边文章开头有链接) :2 种类型的参数含义是不一样的

Mysqldump参数大全  这 些参数 不同于 mysql 的那些参数  :2 种类型的参数含义是不一样的 Mysqldump参数大全(参数来源于mysql5.5.19源码) 参数 参数说明 --all-databases , -A 导出全部数据库. mysqldump -uroot -p --all-databases --all-tablespaces , -Y 导出全部表空间. mysqldump -uroot -p --all-databases --all-tablespaces -

mysql命令行参数 --- 这些参数不同于 mysqldump 后的 那些参数(下边文章开头有链接) :2种类型的参数 含义是不一样的

mysql命令行参数  --- 这些参数不同于  mysqldump  后的 那些参数   :2种类型的参数 含义是不一样的 一,mysql命令行参数 Usage: mysql [OPTIONS] [database] //命令方式 -?, --help //显示帮助信息并退出 -I, --help //显示帮助信息并退出 --auto-rehash //自动补全功能,就像linux里面,按Tab键出提示差不多,下面有例子 -A, --no-auto-rehash //默认状态是没有自动补全功能

802.11学习笔记1-WIFI参数含义

研究下wifi参数的含义 #The word of "Default" must not be removed Default CountryRegion=5 CountryRegionABand=7 CountryCode= SSID=Dennis2860AP NetworkType=Infra WirelessMode=9 Channel=0 BeaconPeriod=100 TxPower=100 BGProtection=0 TxPreamble=0 RTSThreshold=

erlang cpu 相关参数调整

erlang 默认启动参数虽然已经比较高效,但更多的是针对电信平台实时特性,简单调整参数能够很大程度减少CPU,提高处理能力. 1. 关闭spin_wait 设置参数:+sbwt none 我上一篇文章提到:erlang 调度器CPU利用率低排查,关闭spin_wait 可能增加了调度器需要唤醒延时. 关闭后CPU 基本能够简单30%以上,且几乎没有任何副作用,调度器唤醒微妙级,延时可忽略. 2. 调度器唤醒策略 默认设置:+swt medium 默认设置下,长时间运行后部分node运行进入一种

jQuery中Ajax事件beforesend及各参数含义

jQuery中Ajax事件beforesend及各参数含义 2014-01-21 22:27 1678人阅读 评论(0) 收藏 举报  分类: jquery(36)  转自:http://blog.sina.com.cn/s/blog_609f9fdd0100wprz.html Ajax会触发很多事件. 有两种事件,一种是局部事件,一种是全局事件: 局部事件:通过$.ajax来调用并且分配. $.ajax({ beforeSend: function(){ // Handle the befor

php编译参数选项 具体参数含义可以用./configure --help来查看

php编译参数选项 PHP_INSTALL_PATH=/data/web/php MYSQL_INSTALL_PATH=/data/web/mysql ./configure --prefix=${PHP_INSTALL_PATH} --with-config-file-path=${PHP_INSTALL_PATH}/etc --with-mysql=${MYSQL_INSTALL_PATH} --with-mysqli=${MYSQL_INSTALL_PATH}/bin/mysql_conf

java socket 参数

Java socket参数选项: TCP_NODELAY:  表示立即发送数据 SO_RESUSEADDR:  表示是否允许重用socket所绑定的本地地址 SO_TIMEOUT :  表示接收数据时的等待超时时间 . 单位为毫秒,默认值为0 , 表示永远等待. SO_LINGER:  表示当执行 socket的close方法的时候,是否立即关闭底层的socket . 这个单位是秒. SO_SNFBUF:  发送数据的缓冲区大小 SO_RCVBUF:  接收数据的缓冲区大小 SO_KEEPLIV

AdapterView&lt;?&gt; arg0, View arg1, int arg2, long arg3参数含义

arg0:是指父Vjew arg1就是你点击的那个Item的View arg2是position,position是你适配器里面的position arg3是id,一般是第几个项,id是哪个项View的id. AdapterView<?> arg0, View arg1, int arg2, long arg3参数含义

百度搜索URL参数含义

百度搜索URL参数含义 (2014-08-19 15:46:13) 转载▼   分类: Android开发 http://blog.csdn.net/followingturing/article/details/7850317 http://www.baidu.com/s?wd=justin&rsv_spt=1&issp=1&rsv_bp=0&ie=utf-8&tn=baiduhome_pg&rsv_sug3=6&rsv_sug9=eb&r