2017-05-15 85 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>