• 基于TCP的RPC服务


    TCP服务器上的RPC,通过创建一个服务器进程监听传入的tcp连接,并允许用户 通过此TCP流执行RPC命令

    1. -module(tr_server).
    2. -author("chen").
    3. -behaviour(gen_server).
    4. %% API
    5. -export([
    6. start_link/1,
    7. start_link/0,
    8. get_count/0,
    9. stop/0
    10. ]).
    11. -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
    12. terminate/2, code_change/3]).
    13. -define(SERVER, ?MODULE).
    14. -define(DEFAULT_PORT, 8000).
    15. -record(state, {port, lsock, request_count = 0}).
    16. start_link(Port) ->
    17. gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
    18. start_link() ->
    19. start_link(?DEFAULT_PORT).
    20. get_count() ->
    21. gen_server:call(?SERVER, get_count).
    22. stop() ->
    23. gen_server:cast(?SERVER, stop).
    24. init([Port]) ->
    25. {ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
    26. {ok, #state{port = Port, lsock = LSock}, 0}.
    27. handle_call(get_count, _From, State) ->
    28. {reply, {ok, State#state.request_count}, State}.
    29. handle_cast(stop, State) ->
    30. {stop, normal, State}.
    31. handle_info({tcp, Socket, RawData}, State) ->
    32. do_rpc(Socket, RawData),
    33. RequestCount = State#state.request_count,
    34. {noreply, State#state{request_count = RequestCount + 1}};
    35. handle_info(timeout, #state{lsock = LSock} = State) ->
    36. {ok, _Sock} = gen_tcp:accept(LSock),
    37. {noreply, State}.
    38. terminate(_Reason, _State) ->
    39. ok.
    40. code_change(_OldVsn, State, _Extra) ->
    41. {ok, State}.
    42. do_rpc(Socket, RawData) ->
    43. try
    44. {M, F, A} = split_out_mfa(RawData),
    45. Result = apply(M, F, A),
    46. gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Result]))
    47. catch
    48. _Class:Err ->
    49. gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Err]))
    50. end.
    51. split_out_mfa(RawData) ->
    52. MFA = re:replace(RawData, "\r\n$", "", [{return, list}]),
    53. {match, [M, F, A]} =
    54. re:run(MFA,
    55. "(.*):(.*)\s*\\((.*)\s*\\)\s*.\s*$",
    56. [{capture, [1,2,3], list}, ungreedy]),
    57. {list_to_atom(M), list_to_atom(F), args_to_terms(A)}.
    58. args_to_terms(RawArgs) ->
    59. {ok, Toks, _Line} = erl_scan:string("[" ++ RawArgs ++ "]. ", 1),
    60. {ok, Args} = erl_parse:parse_term(Toks),
    61. Args.

     处理TCP上的RPC请求

    do_rpc(Socket, RawData) ->
      try
        {M, F, A} = split_out_mfa(RawData),
        Result = apply(M, F, A),
        gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Result]))
      catch
        _Class:Err ->
          gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Err]))
      end.

     在 split_out_mfa(RawData)中,解析请求数据,随后,将模块名、函数名、参数项式列表传给内置函数apply/,执行请求中的调用。最后,该函数的返回值由io_lib:fwrite/2转换为格式化文本,用作回传给用户的响应,通过套接字发送回去。

    split_out_mfa(RawData) ->
      MFA = re:replace(RawData, "\r\n$", "", [{return, list}]),
      {match, [M, F, A]} =
        re:run(MFA,
          "(.*):(.*)\s*\\((.*)\s*\\)\s*.\s*$",
          [{capture, [1,2,3], list}, ungreedy]),
      {list_to_atom(M), list_to_atom(F), args_to_terms(A)}.

    在 split_out_mfa(RawData)中,解析请求中的字符串。

    启动服务器

    利用xshell向8000端口创建一个连接。

  • 相关阅读:
    Linux文件描述符和打开文件之间的关系
    vue静态资源的引用(相对路径,绝对路径,@,~的一些笔记,以图片引入为例,含在线演示)
    2023CCPC网络赛(A E)
    手把手带你刷好题(牛客刷题⑤)
    越权漏洞知识点
    DNS压测工具-dnsperf的安装和使用(centos)
    阿里云的ACP认证与ACE认证含金量高吗?
    【odoo15】前端自定义模态弹窗
    HtmlAgilityPack中使用xpath获取属性值
    php学习之字符串与数组
  • 原文地址:https://blog.csdn.net/weixin_48283045/article/details/133897553