handle_info()
涉及到達gen_server
郵箱的未知消息(即未處理的消息)。但是當進程以被動模式打開套接字時:{active, false}
,發送到套接字的消息不會落入進程的郵箱中。相反,該進程必須通過調用gen_udp:recv(Socket, Length)
手動讀取套接字中的消息。畢竟,創建一個被動套接字的關鍵是防止郵件淹沒進程的郵箱。因此,當客戶端向被動套接字發送消息時,不會調用handle_info()
。
此外,由於gen_server
是事件驅動,因此您需要撥打gen_udp:recv(Socket, Length)
來回應某些事件。例如,你可以定義服務器功能:
process_message() ->
gen_server:cast(?MODULE, process_msg).
handle_cast(process_msg, #state{socket=Socket} = State) ->
Data = gen_udp:recv(Socket, 0),
io:format("Server received data ~p from socket ~p~n", [Data, Socket]),
{noreply, State}.
然後,你需要有人來定期調用process_message()
。以下似乎工作:
start() ->
io:format("start~n"),
{ok, Server} = gen_server:start_link({local,?MODULE}, ?MODULE, [], []),
Poller = spawn(?MODULE, poll, []), %%<***** HERE *****
io:format("Server: ~w~nPoller: ~w~n", [Server,Poller]).
...
...
handle_cast(process_msg, #state{socket=Socket} = State) ->
case gen_udp:recv(Socket, 10000, 500) of
{error, timeout} -> %%Timeout message.
ok;
{error, Error} ->
io:format("Error: ~p~n", [Error]);
Data ->
io:format("Server received data ~p from socket ~p~n", [Data, Socket])
end,
{noreply, State}.
poll() ->
timer:sleep(1000),
process_message(),
poll().
至於Length
在recv()
,我不知道你應該怎麼指定:我試過0,2,和10000,我看不出在區別。
這裏是我的客戶:
client() ->
Port = 15000,
{ok, Socket} = gen_udp:open(0, [binary, {active, false}]),
gen_udp:send(Socket, "localhost", Port, "hello").
注意open(0, ....)
指示二郎打開任何空閒的端口(客戶端和服務器,如果在同一臺計算機上運行無法打開相同的端口 - 相反,你需要什麼一個gen_tcp
插座)。但是,gen_udp:send()
必須指定與服務器打開的端口相同的端口。此外,原子localhost
和列表"localhost"
都適合我。
完整服務器代碼:
-module(s2).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([start/0, process_message/0, poll/0]).
-record(state, {socket,
port,
local_ip,
broad_ip}).
%%======== PASSIVE SOCKET: {active,false} ===========
%% External interface:
start() ->
io:format("start~n"),
{ok, Server} = gen_server:start_link({local,?MODULE}, ?MODULE, [], []),
Poller = spawn(?MODULE, poll, []),
io:format("Server: ~w~nPoller: ~w~n", [Server,Poller]).
process_message() ->
gen_server:cast(?MODULE, process_msg).
poll() ->
timer:sleep(1000),
process_message(),
poll().
%%Internal server methods:
init([]) ->
Port = 15000,
{ok, Socket} = gen_udp:open(Port, [binary,
{active, false},
{reuseaddr, true}]),
{ok, #state{socket = Socket, port = Port}}.
handle_cast(process_msg, #state{socket=Socket} = State) ->
case gen_udp:recv(Socket, 10000, 500) of
{error, timeout} -> %%Timeout message.
ok;
{error, Error} ->
io:format("Error: ~p~n", [Error]);
Data ->
io:format("Server received data ~p from socket ~p~n", [Data, Socket])
end,
{noreply, State}.
handle_call(_Request, _From, State) ->
{noreply, State}.
handle_info(Msg, State) ->
io:format("Msg: ~w, State:~w~n", [Msg, State]),
{noreply, State}.
terminate(_Reason, #state{socket = LSocket}) ->
gen_udp:close(LSocket).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
在外殼:
殼#1 ---
$ erl
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2 (abort with ^G)
1> c(s2).
{ok,s2}
2> s2:start().
start
Server: <0.64.0>
Poller: <0.65.0>
ok
殼#2--
$ erl
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2 (abort with ^G)
1> c(c2).
{ok,c2}
2> c2:client().
ok
殼# 1-
Server received data {ok,{{127,0,0,1},61841,<<"hello">>}} from socket #Port<0.2110>
3>
殼#2--
3> c2:client().
ok
4>
殼#1--
Server received data {ok,{{127,0,0,1},63983,<<"hello">>}} from socket #Port<0.2110>
3>
有你累[沖洗](http://learnyousomeerlang.com/buckets-of-sockets #highlighter_929390)這些消息並查看它是否到達了您擁有gen_server的節點?你有什麼錯誤信息? – matov
@matov我嘗試了沖洗,但我什麼都沒有,但「好」。 – billcyz