erlang udp服务器无法接收接收数据包

时间:2017-05-15 06:24:58

标签: sockets erlang udp

我有一个用gen_server行为编写的简单udp服务器。当我运行它,并尝试使用gen_udp:send发送消息时,服务器什么也不回复,好像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上启动服务器:gen_udp_server:start_link(10000).

从192.168.146.128发送消息:

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

udp服务器在接收数据包时应该打印一些消息,但是我的服务器失败了。任何人都可以帮助我吗?

1 个答案:

答案 0 :(得分:2)

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和10,000,我无法辨别出差异。

这是我的客户:

client() ->
    Port = 15000,
    {ok, Socket} = gen_udp:open(0, [binary, {active, false}]),
    gen_udp:send(Socket, "localhost", Port, "hello").

请注意open(0, ....)指示erlang打开任何空闲端口(如果在同一台计算机上运行,​​客户端和服务器无法打开相同的端口 - 与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}.

在shell中:

shell#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

shell#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

shell#1 -

Server received data {ok,{{127,0,0,1},61841,<<"hello">>}} from socket #Port<0.2110>
3> 

shell#2 -

3> c2:client().
ok
4> 

shell#1 -

Server received data {ok,{{127,0,0,1},63983,<<"hello">>}} from socket #Port<0.2110>
3> 
相关问题