erlang网络库
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

129 行
4.1 KiB

5 年前
5 年前
5 年前
5 年前
5 年前
5 年前
5 年前
5 年前
5 年前
5 年前
  1. -module(nlUdpIns).
  2. -export([
  3. server/4
  4. , count_peers/1
  5. , stop/1
  6. ]).
  7. -export([
  8. init/1
  9. , handleMsg/2
  10. , terminate/2
  11. ]).
  12. -record(state, {proto, sock, port, peers, mfa}).
  13. -define(ERROR_MSG(Format, Args),
  14. error_logger:error_msg("[~s]: " ++ Format, [?MODULE | Args])).
  15. %%--------------------------------------------------------------------
  16. %% API
  17. %%--------------------------------------------------------------------
  18. -spec(server(atom(), esockd:listen_on(), [gen_udp:option()], mfa())
  19. -> {ok, pid()} | {error, term()}).
  20. server(Proto, Port, Opts, MFA) when is_integer(Port) ->
  21. gen_server:start_link(?MODULE, [Proto, Port, Opts, MFA], []);
  22. server(Proto, {Host, Port}, Opts, MFA) when is_integer(Port) ->
  23. IfAddr = case proplists:get_value(ip, Opts) of
  24. undefined -> proplists:get_value(ifaddr, Opts);
  25. Addr -> Addr
  26. end,
  27. (IfAddr == undefined) orelse (IfAddr = Host),
  28. gen_server:start_link(?MODULE, [Proto, Port, merge_addr(Host, Opts), MFA], []).
  29. merge_addr(Addr, Opts) ->
  30. lists:keystore(ip, 1, Opts, {ip, Addr}).
  31. -spec(count_peers(pid()) -> integer()).
  32. count_peers(Pid) ->
  33. gen_server:call(Pid, count_peers).
  34. -spec(stop(pid()) -> ok).
  35. stop(Pid) ->
  36. gen_server:stop(Pid, normal, infinity).
  37. %%--------------------------------------------------------------------
  38. %% gen_server callbacks
  39. %%--------------------------------------------------------------------
  40. init([Proto, Port, Opts, MFA]) ->
  41. process_flag(trap_exit, true),
  42. case gen_udp:open(Port, esockd_util:merge_opts([binary, {reuseaddr, true}], Opts)) of
  43. {ok, Sock} ->
  44. %% Trigger the udp_passive event
  45. inet:setopts(Sock, [{active, 1}]),
  46. %% error_logger:info_msg("~s opened on udp ~p~n", [Proto, Port]),
  47. {ok, #state{proto = Proto, sock = Sock, port = Port, peers = #{}, mfa = MFA}};
  48. {error, Reason} ->
  49. {stop, Reason}
  50. end.
  51. handleMsg(count_peers, _From, State = #state{peers = Peers}) ->
  52. {reply, maps:size(Peers) div 2, State, hibernate};
  53. handleMsg(Req, _From, State) ->
  54. ?ERROR_MSG("unexpected call: ~p", [Req]),
  55. {reply, ignored, State}.
  56. handleMsg({udp, Sock, IP, InPortNo, Packet},
  57. State = #state{sock = Sock, peers = Peers, mfa = {M, F, Args}}) ->
  58. Peer = {IP, InPortNo},
  59. case maps:find(Peer, Peers) of
  60. {ok, Pid} ->
  61. Pid ! {datagram, self(), Packet},
  62. {noreply, State};
  63. error ->
  64. try erlang:apply(M, F, [{udp, self(), Sock}, Peer | Args]) of
  65. {ok, Pid} ->
  66. _Ref = erlang:monitor(process, Pid),
  67. Pid ! {datagram, self(), Packet},
  68. {noreply, store_peer(Peer, Pid, State)};
  69. {error, Reason} ->
  70. ?ERROR_MSG("Error returned. udp channel: ~s, reason: ~p",
  71. [esockd_net:format(Peer), Reason]),
  72. {noreply, State}
  73. catch
  74. _Error:Reason ->
  75. ?ERROR_MSG("Failed to start udp channel: ~s, reason: ~p",
  76. [esockd_net:format(Peer), Reason]),
  77. {noreply, State}
  78. end
  79. end;
  80. handleMsg({udp_passive, Sock}, State) ->
  81. %% TODO: rate limit here?
  82. inet:setopts(Sock, [{active, 100}]),
  83. {noreply, State, hibernate};
  84. handleMsg({'DOWN', _MRef, process, DownPid, _Reason}, State = #state{peers = Peers}) ->
  85. case maps:find(DownPid, Peers) of
  86. {ok, Peer} ->
  87. {noreply, erase_peer(Peer, DownPid, State)};
  88. error -> {noreply, State}
  89. end;
  90. handleMsg({datagram, Peer = {IP, Port}, Packet}, State = #state{sock = Sock}) ->
  91. case gen_udp:send(Sock, IP, Port, Packet) of
  92. ok -> ok;
  93. {error, Reason} ->
  94. ?ERROR_MSG("Dropped packet to: ~s, reason: ~s", [esockd_net:format(Peer), Reason])
  95. end,
  96. {noreply, State};
  97. handleMsg(Info, State) ->
  98. ?ERROR_MSG("unexpected info: ~p", [Info]),
  99. {noreply, State}.
  100. terminate(_Reason, #state{sock = Sock}) ->
  101. gen_udp:close(Sock).
  102. store_peer(Peer, Pid, State = #state{peers = Peers}) ->
  103. State#state{peers = maps:put(Pid, Peer, maps:put(Peer, Pid, Peers))}.
  104. erase_peer(Peer, Pid, State = #state{peers = Peers}) ->
  105. State#state{peers = maps:remove(Peer, maps:remove(Pid, Peers))}.