zoukankan      html  css  js  c++  java
  • erlang中使用google protobuf进行通信

     

    erlang中使用google protobuf进行通信

    http://www.codedump.info/?p=231

    初学erlang,花了不少的功夫,想要在erlang中集成google的protobuf用于消息通信.个人觉得,使用类似protobuf这样通用的编解码模块,有一个好处就是这部分完全交给别人,再不用自己关心什么很操蛋的大小端,数据长度等琐碎的问题,另外,protobuf使用.proto文件自描述协议,C/S端人员可以通过这个来讨论问题,一目了然.

    然而,要把它集成到erlang中还是一件比较麻烦的事情,一来google官方没有对erlang进行支持,这也许是因为google官方认定的编程语言只有C++,java,Python三种的缘故吧,而它的竞争对手,如thrift等都提供了erlang的支持.虽然官网上给出了第三方做的其他语言的实现,但是毕竟不是官方的实现,可能会有些未知的问题.比如我很担心我使用erlang非官方的protobuf实现写了一个服务器,但是再用比如python写了一个客户端,C/S两端都使用protobuf进行通信,但是由于编解码实现的差异,导致了协议数据有出入.

    不过,鉴于我在搜索资料的时候未发现比较好的介绍如何在erlang中使用protobuf的方式,还是记录一下吧.

    1) 使用哪个protobuf的eralng实现
    google官网上提供了两个protobuf的erlang实现,一个erlang-protobuf,一个是piqi,后者没有研究过,我使用的是前者.
    不过很遗憾,貌似前者的官网实现还是有些问题的,但是所幸的是,basho的riak项目也使用erlang编写的,里面就使用到了protobuf,而且好像对原版的erlang-protobuff进行了一些修正,比如会有一个proto-erlang的二进制文件专门用于编译proto文件产生对应的.hrl和.erl文件,所以这里推荐使用riak版本的erlang-protobuff实现.而我这里给出的实现,也是参考riak项目的,从里面将这部分提取出来形成了一个demo版本.

    2) 协议的编解码
    在demo例子中,我定义了一个名为echo.proto的协议文件,里面的定义如下:

    message Echo {
      required string content = 1;
      required int32 value = 2;
    }
    

    这里注意最后的”}”之后不要加”;’号,否则erlang版本的protobuf编译器会报错.会编译生成对应的.hrl文件,里面是一个对应的record定义,因此就可以这样定义一个符合这个record定义的变量了:

    Msg = #echo{content="hello world", value=1}
    

    但是注意,在erlang里面会产生是这样的数据:{echo, {“hello world”, 1}}也就是说,它会自动加上消息类型的原子数据.这样的话,如果要匹配起来则是一个字符串比较的过程,会比较影响效率,因此可以在编码的时候加上一个映射关系,说白了就是指定一个opcode:

    -module(echo_pb_util).
    -compile(export_all).
    
    -include_lib("echo_pb.hrl").
    
    %% Create an iolist of msg code and protocol buffer message
    encode(Msg) when is_atom(Msg) ->
      [msg_code(Msg)];
    encode(Msg) when is_tuple(Msg) ->
      MsgType = element(1, Msg),
      [msg_code(MsgType) | echo_pb:iolist(MsgType, Msg)].
    
    %% Decode a protocol buffer message given its type - if no bytes
    %% return the atom for the message code
    decode(MsgCode, <<>>) ->
      msg_type(MsgCode);
    decode(MsgCode, MsgData) ->
      echo_pb:decode(msg_type(MsgCode), MsgData).
    
    msg_type(0) -> echo;
    msg_type(_) -> undefined.
    
    msg_code(echo) -> 0.
    

    这个文件不是编译器生成的,而是自己阅读riak的代码抽离出来的,感觉这是一个不错的思路,就是在编码的时候将原子编码为一个opcode,解码的时候将opcode解码还原为一个原子.

    最后给出对应的客户端,服务器的核心代码:
    客户端发送部分的代码:

        Msg = #echo{content="hello world", value=1},
        Pkt = echo_pb_util:encode(Msg),
        ok = gen_tcp:send(Socket, Pkt),
    

    服务器端发送部分的代码:

    	    [MsgCode|MsgData] = binary_to_list(Bin),
    	    io:format("code  ~p, data: ~p~n",[MsgCode, MsgData]),
    	    Msg = echo_pb_util:decode(MsgCode, list_to_binary(MsgData)),
    	    #echo{content=Content, value=Value} = Msg,
    	    io:format("content:~p, value:~p~n",[Content, Value]),
    

    完整的代码在这里,其中echo_pb.hrl/erl是由protobuf的erlang编译器生成的代码.

    服务器/客户端的代码比较粗糙,使用erlang程序设计一书的示例代码中抽出来的,凑合着看看吧:)

  • 相关阅读:
    Proj THUDBFuzz Paper Reading: PMFuzz: Test Case Generation for Persistent Memory Programs
    入围 WF 后训练记
    算法竞赛历程
    2021 多校 杭电 第十场
    2021 多校 杭电 第九场
    2021 多校 牛客 第十场
    2021 多校 牛客 第九场
    2021 多校 杭电 第八场
    2021 多校 杭电 第六场
    2021 多校 杭电 第七场
  • 原文地址:https://www.cnblogs.com/xiayong123/p/3717109.html
Copyright © 2011-2022 走看看