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网络编程中使用频率较高的一个参数,例如:
{tcp, Socket, Binary} ->
我们知道,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 1 | 2 | 4 asn1 | cdr | sunrm | fcgi |tpkt |line The meanings of the packet types are as follows: http | http_bin httph | httph_bin |
packet大致意义如下:
raw | 0 没有封包,即不管数据包头,而是根据Length参数接收数据。 1 | 2 | 4 asn1 | cdr | sunrm | fcgi |tpkt|line http | http_bin |
{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
- -module(tcp_test).
- -export([
- start_server/0,
- start_client_unpack/0, start_client_packed/0
- ]).
- -define(PORT, 8888).
- -define(PORT2, 8889).
- start_server()->
- {ok, ListenSocket} = gen_tcp:listen(?PORT, [binary,{active,false}]),
- {ok, ListenSocket2} = gen_tcp:listen(?PORT2, [binary,{active,false},{packet,2}]),
- spawn(fun() -> accept(ListenSocket) end),
- spawn(fun() -> accept(ListenSocket2) end),
- receive
- _ -> ok
- end.
- accept(ListenSocket)->
- case gen_tcp:accept(ListenSocket) of
- {ok, Socket} ->
- spawn(fun() -> accept(ListenSocket) end),
- loop(Socket);
- _ ->
- ok
- end.
- loop(Socket)->
- case gen_tcp:recv(Socket,0) of
- {ok, Data}->
- io:format("received message ~p~n", [Data]),
- gen_tcp:send(Socket, "receive successful"),
- loop(Socket);
- {error, Reason}->
- io:format("socket error: ~p~n", [Reason])
- end.
- start_client_unpack()->
- {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"),
- sleep(1000).
- start_client_packed()->
- {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"),
- sleep(1000).
- sleep(Count) ->
- receive
- after Count ->
- ok
- end.
运行如下:
- C:\>erlc tcp_test.erl
- C:\>erl -s tcp_test start_server
- Eshell V5.10.2 (abort with ^G)
- 1> tcp_test:start_client_packed().
- received message <<"1">>
- received message <<"2">>
- received message <<"3">>
- received message <<"4">>
- received message <<"5">>
- ok
- 2> tcp_test:start_client_unpack().
- received message <<"12345">>
- 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++为例
- BOOL IsBigEndian()
- {
- int a = 0x1234;
- char b = *(char *)&a; //通过将int强制类型转换成char单字节,通过判断起始存储位置
- if( b == 0x12)
- {
- return TRUE;
- }
- return FALSE;
- }
如何转换字节序,以C++为例
- // 32位字数据
- #define LittletoBig32(A) ((( (UINT)(A) & 0xff000000) >> 24) | \
- (( (UINT)(A) & 0x00ff0000) >> 8) | \
- (( (UINT)(A) & 0x0000ff00) << 8) | \
- (( (UINT)(A) & 0x000000ff) << 24))
- // 16位字数据
- #define LittletoBig16(A) (( ((USHORT)(A) & 0xff00) >> 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.
erlang的Socket参数含义的更多相关文章
- (转)hadoop三个配置文件的参数含义说明
hadoop三个配置文件的参数含义说明 1 获取默认配置 配置hadoop,主要是配置core-site.xml,hdfs-site.xml,mapred-site.xml三个配 ...
- Mysqldump参数大全 这 些参数 不同于 mysql 的那些参数(下边文章开头有链接) :2 种类型的参数含义是不一样的
Mysqldump参数大全 这 些参数 不同于 mysql 的那些参数 :2 种类型的参数含义是不一样的 Mysqldump参数大全(参数来源于mysql5.5.19源码) 参数 参数说明 --a ...
- mysql命令行参数 --- 这些参数不同于 mysqldump 后的 那些参数(下边文章开头有链接) :2种类型的参数 含义是不一样的
mysql命令行参数 --- 这些参数不同于 mysqldump 后的 那些参数 :2种类型的参数 含义是不一样的 一,mysql命令行参数 Usage: mysql [OPTIONS] [ ...
- Linux内核 TCP/IP、Socket参数调优
Linux内核 TCP/IP.Socket参数调优 2014-06-06 Harrison.... 阅 9611 转 165 转藏到我的图书馆 微信分享: Doc1: /proc/sy ...
- paip.提升效率--调试--日志系统日志参数含义---python
paip.提升效率--调试--日志系统日志参数含义---python #同时向控制台和文件输出日志 #日志参数含义 import logging log_format = '%(filename)s ...
- 机器学习——随机森林,RandomForestClassifier参数含义详解
1.随机森林模型 clf = RandomForestClassifier(n_estimators=200, criterion='entropy', max_depth=4) rf_clf = c ...
- C关键字typedef及argc,argv,env参数含义
C关键字typedef--为C中各种数据类型定义别名. 在此插一点C知识 int main(int argc,const char *argv[],const char *envp[])主函数的红色部 ...
- 百度搜索URL参数含义
序号 参数 含义 1 tn 搜索框所属网站.比如 tn=sitehao123,就是 http://www.hao123.com/ 左上那个搜索框(指通过什么方式到达百度首页搜索界面;) 2 s?wd ...
- php编译参数选项 具体参数含义可以用./configure --help来查看
php编译参数选项 PHP_INSTALL_PATH=/data/web/php MYSQL_INSTALL_PATH=/data/web/mysql ./configure --prefix=${ ...
随机推荐
- hbs模板(zmaze ui用的)
hbs模板(zmaze ui用的) 一.总结 1.模板引擎:就是来生成界面的啊,只不过实现了view和数据分离以及一些其它的功能(预加载等). 2.Handlebars :但他是一个单纯的模板引擎,在 ...
- Flask项目之手机端租房网站的实战开发(五)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...
- c++中的相对路径
今天在vs2010里读取相对路径下的图片文件出了点问题.于是查了一下相对路径的编程知识,记录下来分享给大家: 问题描写叙述:path=".\\TrainData\\& ...
- IOS总结
1.Difference between shallow copy and deep copy? 浅复制和深复制的区别? 答案:浅层复制:只复制指向对象的指针,而不复制引用对象本身. 深层复制:复制引 ...
- Android 自己定义主菜单
本文介绍一个超简单的自己定义主菜单.效果例如以下: 原理:事实上就是对原生的Dialog的一个简单的封装.并加上显示和隐藏的动画效果.再给控件加上回调事件. TestDialog.java publi ...
- php实现 查找输入整数二进制中1的个数
php实现 查找输入整数二进制中1的个数 一.总结 一句话总结: 1.if($j&intval($num)){}的作用是什么? 1 <?php 2 while($num=trim(fge ...
- UVA 11367 - Full Tank? dijkstra+DP
传送门:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&a ...
- 量化交易中VWAP/TWAP算法的基本原理和简单源码实现(C++和python)(转)
量化交易中VWAP/TWAP算法的基本原理和简单源码实现(C++和python) 原文地址:http://blog.csdn.net/u012234115/article/details/728300 ...
- Ajax之旅(二)--XMLHttpRequest
上文中提到的Ajax的异步更新.主要使用XMLHttpRequest对象来实现的,XMLHttpRequest对象能够在不向server提交整个页面的情况下,实现局部更新网页. 当页面所有载 ...
- GAS Syntax
GAS or GNU as syntax is a different form of syntax for assembly language files, known also as AT& ...