2017-05-15 42 views
1

我有一个用gen_server行为编写的简单的udp服务器。当我运行它,并尝试通过使用gen_udp:send发送消息时,服务器什么也不回答,好像udp服务器没有成功接收数据包。这里是我的代码erlang udp服务器无法接收数据包

gen_udp_server.erl:

-module(gen_udp_server). 
-behaviour(gen_server). 
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 

-export([start_link/1]). 

-define(SERVER, ?MODULE). 

-record(state, {socket, 
       port, 
       local_ip, 
       broad_ip}). 

start_link(Port) -> 
    {ok, Socket} = gen_udp:open(Port, [binary, 
             {active, false}, 
             {reuseaddr, true}]), 
    gen_server:start_link(?MODULE, [Socket, Port], []). 

init([Socket, Port]) -> 
    {ok, #state{socket = Socket, port = Port}}. 

handle_cast(_Request, State) -> 
    {noreply, State}. 

handle_call(_Request, _From, State) -> 
    {noreply, State}. 

handle_info({udp, _Socket, _Addr, _Port, Data}, #state{socket = Socket} = State) -> 
    inet:setopts(Socket, [{active, once}]), 
    io:format("Server received data ~p from socket ~p~n", [Data, Socket]), 
    {ok, State}. 

terminate(_Reason, {socket = LSocket}) -> 
    gen_udp:close(LSocket). 

code_change(_OldVsn, State, _Extra) -> 
    {ok, State}. 

开始对服务器192.168.146.129服务器:从192.168.146.128 gen_udp_server:start_link(10000).

发送消息:

{ok, Socket} = gen_udp:open(4399, [binary, {active, false}]). 
gen_udp:send(Socket, {192,168,146,129}, 10000, "hello"). 

UDP服务器应当它收到数据包时打印一些消息,但我的一个失败了。谁能帮我?

+0

有你累[冲洗](http://learnyousomeerlang.com/buckets-of-sockets #highlighter_929390)这些消息并查看它是否到达了您拥有gen_server的节点?你有什么错误信息? – matov

+0

@matov我尝试了冲洗,但我什么都没有,但“好”。 – billcyz

回答

1

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(). 

至于Lengthrecv(),我不知道你应该怎么指定:我试过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>