erlang自定义二进制协议
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

287 řádky
12 KiB

před 4 roky
před 5 roky
před 4 roky
před 4 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
před 5 roky
  1. genProto
  2. =====
  3. An OTP library
  4. Build
  5. -----
  6. $ rebar3 compile
  7. # 简单描述
  8. 可用于erlang游戏或者erlang其他网络应用的协议解析编码解码的项目,具有较高的编码解码性能,
  9. 已经尽量优化了编码解码的代码使其编码解码速度更快
  10. 协议文件存放在proto目录 文件名为 Message protocol definition file的缩写 mpdf
  11. 源码目录为src,test目录主要用于生成协议和测试的脚本以及用于测试的代码。
  12. ## 二进制语法
  13. Bin = <<E1, E2, ... En>>
  14. <<E1, E2, ... En>> = Bin
  15. 每个E 1..n 指定bitstring的Segment
  16. 每个段具有以下一般语法:
  17. Value:Size/TypeSpecifierList
  18. 可以省略Size或TypeSpecifier或两者。因此,允许以下变体:
  19. Ei =
  20. Value |
  21. Value:Size |
  22. Value/TypeSpecifierList |
  23. Value:Size/TypeSpecifierList
  24. ### Value
  25. 当在二进制构造中使用时,Value部分是任何表达式,用于求值为整数,浮点或位串,如果表达式不是单个文字或变量,则将其括在括号中。
  26. 在二进制匹配中使用时,用于位串匹配,Value必须是变量,或整数,浮点或字符串,简单而言就是Value部分必须是文字或变量。
  27. ### Size
  28. 在位串构造中使用,Size是要求求整数的表达式。
  29. 用于位串匹配,Size必须是整数,或绑定到整数的变量。
  30. Size的值以Unit指定段的大小,默认值取决于类型
  31. • For integer it is 8.
  32. • For float it is 64.
  33. • For binary and bitstring it is the whole binary or bit string.
  34. 在匹配中,此默认值仅对最后一个元素有效。匹配中的所有其他位串或二进制元素必须具有大小规范,段的大小Size部分乘以TypeSpecifierList中的unit(稍后描述)给出了段的位数.对于utf8,utf16和utf32类型,不得给出Size的大小。段的大小由类型和值本身隐式确定。
  35. ### TypeSpecifierList
  36. 是一个类型说明符列表,按任何顺序,用连字符("-")分隔。默认值用于任何省略的类型说明符
  37. #### Type
  38. Type= integer | float | binary | bytes | bitstring | bits | utf8 | utf16 | utf32
  39. 默认值为integer。bytes是二进制的简写,bits是bitstring的简写。有关utf类型的更多信息,请参见下文。
  40. #### Signedness
  41. Signedness= signed | unsigned
  42. 只有匹配和类型为整数时才有意义。默认值为无符号。
  43. #### Endianness
  44. Endianness= big | little | native
  45. Native-endian意味着字节顺序在加载时被解析为big-endian或little-endian,具体取决于运行Erlang机器的CPU的本机内容。仅当Type为integer,utf16,utf32或float时,字节顺序才有意义。默认值为big。
  46. #### Unit
  47. Unit= unit:IntegerLiteral
  48. 允许的范围是1..256。对于integer,float和bitstring,默认值为1;对于binary,默认值为8。对于utf8,utf16和utf32类型,不能给出Unit说明符。
  49. 它与Size说明符相乘,以给出段的有效大小。单位大小指定没有大小的二进制段的对齐方式,二进制类型的段必须具有可被8整除的大小
  50. ### 注意
  51. 构造二进制文件时,如果整数段的大小N太小而不能包含给定的整数,则整数的最高有效位将被静默丢弃,并且只有N个最低有效位被放入二进制。
  52. #### 例子:
  53. X:4/little-signed-integer-unit:8
  54. 该元素的总大小为4 * 8 = 32位,它包含一个小端序的有符号整数
  55. ### 关于 utf8 utf16 utf32
  56. 构造utf类型的段时,Value必须是0..16#D7FF或16#E000 .... 16#10FFFF范围内的整数。如果Value超出允许范围,则构造将失败并返回badarg异常。生成的二进制段的大小取决于类型或值,或两者:
  57. • For utf8, Value is encoded in 1-4 bytes.
  58. • For utf16, Value is encoded in 2 or 4 bytes.
  59. • For utf32, Value is always be encoded in 4 bytes.
  60. 构造时,可以给出一个文字字符串,后跟一个UTF类型,例如:<<“abc”/ utf8 >>,这是<< $ a / utf8,$ b / utf8,$ c / utf8的语法糖>>。
  61. 成功匹配utf类型的段,得到0..16#D7FF或16#E000..16#10FFFF范围内的整数。
  62. 如果返回值超出这些范围,则匹配失败。
  63. ### 如何实现二进制文件
  64. 在内部,二进制和位串以相同的方式实现。
  65. 内部有四种类型的二进制对象:
  66. 两个是二进制数据的容器,称为:
  67. • Refc binaries (short for reference-counted binaries)
  68. • Heap binaries
  69. 两个仅仅是对二进制文件的一部分的引用,被称为:
  70. • sub binaries
  71. • match contexts
  72. ### Refc Binaries
  73. Refc二进制文件由两部分组成:
  74. •存储在进程堆上的对象,称为ProcBin
  75. •二进制对象本身,存储在所有进程堆之外
  76. 任何数量的进程都可以通过任意数量的ProcBins引用二进制对象。该对象包含一个引用计数器,用于跟踪引用的数量,以便在最后一个引用消失时将其删除。
  77. 进程中的所有ProcBin对象都是链表的一部分,因此当ProcBin消失时,垃圾收集器可以跟踪它们并减少二进制文件中的引用计数器。
  78. ### Heap Binaries
  79. 堆二进制文件是小型二进制文件,最多64个字节,并直接存储在进程堆上。它们在进程被垃圾收集时以及作为消息发送时被复制。它们不需要垃圾收集器进行任何特殊处理。
  80. ### Sub Binaries
  81. The reference objects sub binaries and match contexts can reference part of a refc binary or heap binary
  82. 子二进制文件由split_binary / 2创建或者当二进制文件以二进制模式匹配时。子二进制是对另一个二进制文件(refc或堆二进制文件的一部分,但从不进入另一个子二进制文件)的引用。因此,匹配二进制文件相对便宜,因为实际的二进制数据永远不会被复制。
  83. ### Match Context
  84. 匹配上下文类似于子二进制,但针对二进制匹配进行了优化
  85. ### 关于iolist
  86. 定义(直接引用霸业的文章)
  87. 1. []
  88. 2. binary
  89. 3. 列表, 每个元素是int(0-255)或者binary或者iolist.
  90. 其中binary是指 bitsize % 8 == 0 .
  91. int 是0-255
  92. Iolist的作用是用于往port送数据的时候.由于底层的系统调用如writev支持向量写, 就避免了无谓的iolist_to_binary这样的扁平话操作, 避免了内存拷贝,极大的提高了效率.
  93. 另外额外补充:
  94. erlang中列表时在头部添加比较高效,但是binary是在尾部追加更高效
  95. ### 关于消息接收转发解码和发送
  96. erlang通常会将接收到的消息由网关进程转发给其他工作进程, 建议先匹配消息id, 然后转发二进制消息到工作进程,然后由工作进程解码再处理
  97. 同时广播消息可先编码成二进制之后再广播, 避免重复编码
  98. ### 简单性能测评
  99. 主要和gpb做简单对比测试
  100. gpb测试相关文件在test/gpb目录下
  101. 测试协议:
  102. gpb:
  103. message test {
  104. required string aa = 1;
  105. }
  106. message phoneNumber {
  107. required test number = 1;
  108. required int32 type = 2;
  109. }
  110. message person {
  111. required string name = 1;
  112. required int32 integer = 2;
  113. optional string email = 3;
  114. repeated phoneNumber phone = 4;
  115. }
  116. message addressBook {
  117. repeated person person1 = 1;
  118. repeated person others = 2;
  119. }
  120. message tint32 {
  121. required int32 int1 = 1;
  122. required int32 int2 = 2;
  123. required int32 int3 = 3;
  124. required int32 int4 = 4;
  125. required int32 int5 = 5;
  126. required int32 int6 = 6;
  127. required int32 int7 = 7;
  128. required int32 int8 = 8;
  129. required int32 int9 = 9;
  130. required int32 int10 = 10;
  131. }
  132. genProto用的协议:
  133. test {
  134. string aa;
  135. }
  136. phoneNumber{
  137. test number;
  138. int32 type;
  139. }
  140. person{
  141. string name;
  142. int32 id;
  143. string email;
  144. list[phoneNumber] phone;
  145. }
  146. addressBook {
  147. list[person] person;
  148. list[person] other;
  149. }
  150. tint32{
  151. int32 int1;
  152. int32 int2;
  153. int32 int3;
  154. int32 int4;
  155. int32 int5;
  156. int32 int6;
  157. int32 int7;
  158. int32 int8;
  159. int32 int9;
  160. int32 int10;
  161. }
  162. 测试运行三次 每次100万次循环
  163. tint32 gpb-------->>
  164. tint32 encode:
  165. > timer:tc(mytest, encode_int32, [1000000]).
  166. {9625000,ok}
  167. > timer:tc(mytest, encode_int32, [1000000]).
  168. {9000000,ok}
  169. > timer:tc(mytest, encode_int32, [1000000]).
  170. {9969000,ok}
  171. tin32 decode:
  172. > timer:tc(mytest, decode_int32, [1000000]).
  173. {6217994,ok}
  174. > timer:tc(mytest, decode_int32, [1000000]).
  175. {6187993,ok}
  176. > timer:tc(mytest, decode_int32, [1000000]).
  177. {6265994,ok}
  178. size:
  179. > BTInt32 = mytest:decode_int32(1).
  180. <<8,1,16,255,255,255,255,255,255,255,255,255,1,24,128,1,
  181. 32,128,255,255,255,255,255,255,255,255,1,40,128,...>>
  182. 31> byte_size(BTInt32).
  183. 74
  184. tint32 genProto ------->>
  185. tint32 encode:
  186. > timer:tc(test, encode_int32, [1000000]).
  187. {328999,ok}
  188. > timer:tc(test, encode_int32, [1000000]).
  189. {328000,ok}
  190. > timer:tc(test, encode_int32, [1000000]).
  191. {344000,ok}
  192. tint32 decode:
  193. > timer:tc(test, decode_int32, [1000000]).
  194. {328000,ok}
  195. > timer:tc(test, decode_int32, [1000000]).
  196. {328000,ok}
  197. > timer:tc(test, decode_int32, [1000000]).
  198. {329000,ok}
  199. size:
  200. > BTInt32 = test:decode_int32(1).
  201. <<0,11,0,0,0,1,255,255,255,255,0,0,0,128,255,255,255,128,
  202. 0,1,0,0,255,255,0,0,125,43,117,...>>
  203. > byte_size(BTInt32).
  204. 42
  205. ===============================================================================
  206. ===============================================================================
  207. addressBook gpb-------->>
  208. addressBook encode:
  209. > timer:tc(mytest, encode_addressBook, [1000000]).
  210. {9108990,ok}
  211. > timer:tc(mytest, encode_addressBook, [1000000]).
  212. {8999991,ok}
  213. > timer:tc(mytest, encode_addressBook, [1000000]).
  214. {9031991,ok}
  215. addressBook decode:
  216. > timer:tc(mytest, decode_addressBook, [1000000]).
  217. {5702995,ok}
  218. > timer:tc(mytest, decode_addressBook, [1000000]).
  219. {5764994,ok}
  220. > timer:tc(mytest, decode_addressBook, [1000000]).
  221. {5718995,ok}
  222. size:
  223. > BAddr = mytest:decode_addressBook(1).
  224. <<10,43,10,5,65,108,105,99,101,16,144,78,34,15,10,11,10,9,
  225. 49,50,51,52,53,54,55,56,57,16,1,...>>
  226. > byte_size(BAddr).
  227. 75
  228. addressBook genProto -------->>
  229. addressBook encode:
  230. > timer:tc(test, encode_addressBook, [1000000]).
  231. {4186995,ok}
  232. > timer:tc(test, encode_addressBook, [1000000]).
  233. {4202996,ok}
  234. > timer:tc(test, encode_addressBook, [1000000]).
  235. {4202996,ok}
  236. addressBook decode:
  237. > timer:tc(test, decode_addressBook, [1000000]).
  238. {2749997,ok}
  239. > timer:tc(test, decode_addressBook, [1000000]).
  240. {2812997,ok}
  241. > timer:tc(test, decode_addressBook, [1000000]).
  242. {2812997,ok}
  243. size:
  244. BAddr = test:decode_addressBook(1).
  245. <<0,4,0,2,0,5,65,108,105,99,101,0,0,39,16,0,0,0,2,1,0,9,
  246. 49,50,51,52,53,54,55,...>>
  247. 67> byte_size(BAddr).
  248. 83
  249. ### TODO
  250. 生成的protoMsg.erl
  251. encode 函数参数列表太长时 换行显示
  252. encode返回的编码列表参数太多时 换行显示
  253. decodeBin simple类型解码列表过长时 换行显示
  254. decodeBin 返回元组元素太多时 换行显示