Erlang gen_服务器正在关闭侦听套接字
我试图做的是让gen_服务器进程接受一个新的客户端,并立即生成一个新的子进程来处理下一个客户端。我看到的问题是,当套接字完成并最终终止时,它也会关闭侦听套接字,我不知道为什么,即使它不再引用它 知道我做错了什么吗 gen_服务器:Erlang gen_服务器正在关闭侦听套接字,erlang,gen-server,gen-tcp,Erlang,Gen Server,Gen Tcp,我试图做的是让gen_服务器进程接受一个新的客户端,并立即生成一个新的子进程来处理下一个客户端。我看到的问题是,当套接字完成并最终终止时,它也会关闭侦听套接字,我不知道为什么,即使它不再引用它 知道我做错了什么吗 gen_服务器: -module(simple_tcp). -behaviour(gen_server). %% API -export([start_link/1, stop/0, start/0, start/1]). %% gen-server callbacks -expo
-module(simple_tcp).
-behaviour(gen_server).
%% API
-export([start_link/1, stop/0, start/0, start/1]).
%% gen-server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 1055).
-record(state, {port, lsock}).
start_link({port, Port}) ->
gen_server:start_link(?MODULE, [{port, Port}], []);
start_link({socket, Socket}) ->
gen_server:start_link(?MODULE, [{socket, Socket}], []).
start({port, Port}) ->
simple_tcp_sup:start_child({port, Port});
start({socket, Socket}) ->
simple_tcp_sup:start_child({socket, Socket}).
start() ->
start({port, ?DEFAULT_PORT}).
stop() ->
gen_server:cast(?SERVER, stop).
% Callback functions
init([{port, Port}]) ->
{ok, LSock} = gen_tcp:listen(Port, [{active, true},{reuseaddr, true}]),
init([{socket, LSock}]);
init([{socket, Socket}]) ->
io:fwrite("Starting server with socket: ~p~n", [self()]),
{ok, Port} = inet:port(Socket),
{ok, #state{port=Port, lsock=Socket}, 0}.
handle_call(_Msg, _From, State) ->
{noreply, State}.
handle_cast(stop, State) ->
{stop, ok, State}.
handle_info({tcp, Socket, RawData}, State) ->
gen_tcp:send(Socket, io_lib:fwrite("Received raw data: ~p~n", [RawData])),
{noreply, State};
handle_info({tcp_error, _Socket, Reason}, State) ->
io:fwrite("Error: ~p~n", [Reason]),
{stop, normal, State};
handle_info(timeout, #state{lsock = LSock} = State) ->
case gen_tcp:accept(LSock) of
{ok, Sock} ->
io:fwrite("Accepting connection...~p~n", [self()]),
start({socket, LSock}),
{noreply, #state{lsock=Sock}};
{error, Reason} ->
io:fwrite("Error: ~p, ~p~n", [Reason, self()]),
{stop, normal, State}
end;
handle_info({tcp_closed, _Port}, State) ->
io:fwrite("Socket closed: ~p~n", [self()]),
simple_tcp_sup:kill_child(self()),
{stop, normal, State}.
terminate(_Reason, _State) ->
io:fwrite("Shutting down server: ~p~n", [self()]),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
主管:
-module(simple_tcp_sup).
-behaviour(supervisor).
-export([start_link/0,
start_child/1
]).
-export([init/1]).
-define(SERVER, ?MODULE).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_child({socket, Socket}) ->
io:fwrite("Spawning child with socket...~n"),
supervisor:start_child(?SERVER, [{socket, Socket}]);
start_child({port, Port}) ->
io:fwrite("Spawning child with port...~n"),
supervisor:start_child(?SERVER, [{port, Port}]).
init([]) ->
Element = {simple_tcp, {simple_tcp, start_link, []},
temporary, brutal_kill, worker, [simple_tcp]},
Children = [Element],
RestartStrategy = {simple_one_for_one, 0, 1},
{ok, {RestartStrategy, Children}}.
您的第三个
句柄\u info
反转了Sock
和LSock
的角色。它应该将Sock
传递给子进程,并保持自己的状态不变
顺便说一句:从头开始重建
State
(#State{lsock=Sock}
)是一件坏事,你应该总是从当前的State
(State}State{lsock=Sock}
)派生新的状态,以防万一你以后添加更多的状态变量。实际上,这现在有一个bug(尽管是良性的),因为您正在丢弃端口号。好吧,我建议您让套接字由单独的进程来处理,这些进程与gen_服务器异步通信,并与之链接。我有一个示例代码片段,可以向您展示如何做到这一点。gen_服务器启动并生成一个TCP侦听器,该侦听器在成功获取侦听套接字后通知我们的gen_服务器更改其内部状态。
我已经从上到下排列了代码。所有相关功能均已显示。
重点关注套接字处理过程以及它们如何与gen_服务器交互
-define(PEER_CLIENT_TIMEOUT,timer:seconds(20)).
-define(PORT_RANGE,{10245,10265}).
-define(DEBUG(X,Y),error_logger:info_msg(X,Y)).
-define(ERROR(L),error_logger:error_report(L)).
-define(SOCKET_OPTS(IP),[inet,binary,{backlog,100},{packet,0},
{reuseaddr,true},{active,true},
{ip,IP}]).
%%----------------------------------------------------
%% gen_server starts here....
start(PeerName)->
gen_server:start_link({local,?MODULE},?MODULE,PeerName,[]).
%%%-------------------------------------------
%% Gen_server init/1 function
init(PeerName)->
process_flag(trap_exit,true),
%% starting the whole Socket chain below..
start_link_listener(),
%% Socket stuff started, gen_server can now wait for async
%% messages
{ok,[]}.
%%% ---- Socket handling functions ---------
%% Function: start_link_listener/0
%% Purpose: Starts the whole chain of listening
%% and waiting for connections. Executed
%% directly by the gen_server process, But
%% spawns a separate process to do the rest
start_link_listener()->
Ip_address = get_myaddr(),
spawn_link(fun() -> listener(?SOCKET_OPTS(Ip_address)) end).
%%%----------------------------------------------
%% Function: get_myaddr/0
%% Purpose: To pick the active IP address on my machine to
%% listen on
get_myaddr()->
?DEBUG("Server> Trying to extract My Local Ip Address....",[]),
{ok,Name} = inet:gethostname(),
{ok,IP} = inet:getaddr(Name,inet),
?DEBUG("Server> Found Alive Local IP address: ~p.....~n",[IP]),
IP.
%%%--------------------------------------------------
%% Function: listener/1, executed in a separate process
%% Purpose: Tries a given ?PORT_RANGE, with the given Socket Options
%% Once it acquires a ListenSocket, it will cast the gen_server!
listener(SocketOpts)->
process_flag(trap_exit,true),
Ports = lists:seq(element(1,?PORT_RANGE),element(2,?PORT_RANGE)),
case try_listening(SocketOpts,Ports) of
{ok,Port,LSocket}->
PP = proplists:get_value(ip,SocketOpts),
?MODULE:started_listener(Port,PP,LSocket),
accept_connection(LSocket);
{error,failed} -> {error,failed,SocketOpts}
end.
try_listening(_Opts,[])-> {error,failed};
try_listening(Opts,[Port|Rest])->
case gen_tcp:listen(Port,Opts) of
{ok,Listen_Socket} -> {ok,Port,Listen_Socket};
{error,_} -> try_listening(Opts,Rest)
end.
%%%---------------------------------------------------------
%% Helper Functions for Converting IP Address from tuple
%% to string and vice versa
str(X) when is_integer(X)-> integer_to_list(X).
formalise_ipaddress({A,B,C,D})->
str(A) ++ "." ++ str(B) ++ "." ++ str(C) ++ "." ++ str(D).
unformalise_address(String)->
[A,B,C,D] = string:tokens(String,"."),
{list_to_integer(A),list_to_integer(B),list_to_integer(C),list_to_integer(D)}.
%%%--------------------------------------------------
%% Function: get_source_connection/1
%% Purpose: Retrieving the IP and Port at the other
%% end of the connection
get_source_connection(Socket)->
try inet:peername(Socket) of
{ok,{IP_Address, Port}} ->
[{ipAddress,formalise_ipaddress(IP_Address)},{port,Port}];
_ -> failed_to_retrieve_address
catch
_:_ -> failed_to_retrieve_address
end.
%%%-----------------------------------------------------
%% Function: accept_connection/1
%% Purpose: waits for a connection and re-uses the
%% ListenSocket by spawning another thread
%% to take it and listen too. It casts the gen_server
%% at each connection and provides details about it.
accept_connection(ListenSocket)->
case gen_tcp:accept(ListenSocket,infinity) of
{ok, Socket}->
%% re-use the ListenSocket below.....
spawn_link(fun() -> accept_connection(ListenSocket) end),
OtherEnd = get_source_connection(Socket),
?MODULE:accepted_connection(OtherEnd),
loop(Socket,OtherEnd);
{error,_} = Reason ->
?ERROR(["Listener has failed to accept a connection",
{listener,self()},{reason,Reason}])
end.
%%%-------------------------------------------------------------------------
%% Function: loop/2
%% Purpose: TCP reception loop, it casts the gen_server
%% as soon as it receives something. gen_server
%% is responsible for generating reponse
%% OtherEnd ::= [{ipAddress,StringIPAddress},{Port,Port}] or 'failed_to_retrieve_address'
loop(Socket,OtherEnd)->
receive
{tcp, Socket, Data}->
?DEBUG("Acceptor: ~p has received a binary message from: ~p~n",[self(),OtherEnd]),
Reply = ?MODULE:incoming_binary_message(Data,OtherEnd),
gen_tcp:send(Socket,Reply),
gen_tcp:close(Socket),
exit(normal);
{tcp_closed, Socket} ->
?DEBUG("Acceptor: ~p. Socket closed by other end: ~p~n",[self(),OtherEnd]),
?MODULE:socket_closed(OtherEnd),
exit(normal);
Any -> ?DEBUG("Acceptor: ~p has received a message: ~p~n",[self(),Any])
end.
%%%----------------------------------------------
%% Gen_server Asynchronous APIs
accepted_connection(failed_to_retrieve_address)-> ok;
accepted_connection([{ipAddress,StringIPAddress},{Port,Port}])->
gen_server:cast(?MODULE,{connected,StringIPAddress,Port}).
socket_closed(failed_to_retrieve_address)-> ok;
socket_closed([{ipAddress,StringIPAddress},{Port,Port}])->
gen_server:cast(?MODULE,{socket_closed,StringIPAddress,Port}).
incoming_binary_message(Data,_OtherEnd)-> %% expecting a binary reply
case analyse_protocol(Data) of
wrong -> term_to_binary("protocol violation!");
Val -> gen_server:call(?MODULE,{request,Val},infinity)
end.
%%% -------------------- handle cast ------------------------------------------
handle_cast({listener_starts,_Port,_MyTupleIP,_LSocket} = Object,State)->
NewState = do_something_with_the_listen_report(Object),
{noreply,NewState};
handle_cast({connected,_StringIPAddress,_Port} = Object,State)->
NewState = do_something_with_the_connection_report(Object),
{noreply,NewState};
handle_cast({socket_closed,_StringIPAddress,_Port} = Object,State)->
NewState = do_something_with_the_closed_connection_report(Object),
{noreply,NewState};
handle_cast(Any,State)->
?DEBUG("Server> I have been casted some unknown message: ~p~n",[Any]),
{noreply,State}.
%%%% ---------------------- handle call --------------
handle_call({request,Val},_,State)->
{NewState,Reply} = req(Val,State),
{reply,Reply,NewState};
handle_call(_,_,State)-> {reply,[],State}.
req(Val,State)->
%% modify gen_server state and
%% build reply
{NewState,Reply} = modify_state_and_get_reply(State,Val),
{NewState,Reply}.
%%------------------- terminate/2 --------------------
terminate(_Reason,_State)-> ok.
%%----------------- code_change/3 ------------------
code_change(_,State,_)-> {ok,State}.
-定义(对等客户端超时,计时器:秒(20))。
-定义(端口范围,{1024510265})。
-定义(调试(X,Y),错误\记录器:信息\消息(X,Y))。
-定义(错误(L),错误日志:错误报告(L))。
-定义(套接字选择(IP),[inet,二进制,{backlog,100},{packet,0},
{reuseaddr,true},{active,true},
{ip,ip}])。
%%----------------------------------------------------
%%gen_服务器从这里开始。。。。
开始(PeerName)->
gen_服务器:启动链接({local,?MODULE},?MODULE,PeerName,[])。
%%%-------------------------------------------
%%Gen_服务器初始化/1功能
初始化(对等名称)->
进程_标志(陷阱_退出,真),
%%启动下面的整个插座链。。
启动链接侦听器(),
%%套接字启动,gen_服务器现在可以等待异步
%%信息
{好的,[]}。
%%%----套接字处理函数---------
%%功能:启动链接侦听器/0
%%目的:启动整个倾听链
%%等待连接。执行
%%直接由gen_服务器进程执行,但是
%%生成一个单独的进程来完成其余的工作
启动链接侦听器()->
Ip_address=get_myaddr(),
生成链接(fun()->侦听器(?SOCKET_选项(Ip_地址))结束)。
%%%----------------------------------------------
%%功能:获取\u myaddr/0
%%用途:在我的计算机上选择活动IP地址以
%%听
获取\u myaddr()->
?调试(“服务器>尝试提取我的本地Ip地址…”,[]),
{ok,Name}=inet:gethostname(),
{ok,IP}=inet:getaddr(名称,inet),
?调试(“服务器>找到活动的本地IP地址:~p…~n”,[IP]),
知识产权。
%%%--------------------------------------------------
%%函数:listener/1,在单独的进程中执行
%%用途:使用给定的套接字选项尝试给定的?端口_范围
%%一旦获得ListenSocket,它将强制转换gen_服务器!
侦听器(SocketOpts)->
进程_标志(陷阱_退出,真),
端口=列表:seq(元素(1,端口范围),元素(2,端口范围)),
正在侦听的案例(SocketOpts、端口)
{好的,端口,LSocket}->
PP=属性列表:获取_值(ip、SocketOpts),
?模块:启动\u侦听器(端口、PP、LSocket),
接受_连接(LSocket);
{error,failed}->{error,failed,SocketOpts}
结束。
尝试侦听(_Opts,[])->{error,failed};
尝试侦听(选择[Port|Rest])->
案例gen_tcp:侦听(端口、选项)
{ok,Listen_Socket}->{ok,Port,Listen_Socket};
{error,{}->尝试侦听(Opts,Rest)
结束。
%%%---------------------------------------------------------
%%用于从元组转换IP地址的帮助器函数
%%串
str(X)何时为整数(X)->整数到列表(X)。
形式化IP地址({A,B,C,D})->
str(A)++”++str(B)++”++str(C)++”++str(D)。
不格式化_地址(字符串)->
[A,B,C,D]=字符串:标记(字符串“.”),
{list_to_integer(A)、list_to_integer(B)、list_to_integer(C)、list_to_integer(D)}。
%%%--------------------------------------------------
%%功能:获取\u源\u连接/1
%%目的:检索另一个服务器上的IP和端口
%%连接结束
获取\u源\u连接(套接字)->
尝试inet:peername(套接字)的
{好,{IP_地址,端口}}->
[{ipAddress,formalise_-ipAddress(IP_-Address)},{port,port}];
_->检索地址失败
抓住
_:u->检索地址失败
结束。
%%%-----------------------------------------------------
%%功能:接受\u连接/1
%%目的:等待连接并重新使用
%%通过生成另一个线程来ListenSocket
%%接受它,也倾听它。它强制转换gen_服务器
%%并提供了有关它的详细信息。
接受\u连接(ListenSocket)->
case gen_tcp:accept(ListenSocket,无穷大)
{好的,套接字}->
%%重新使用下面的ListenSocket。。。。。
生成链接(fun()->接受连接(ListenSocket)结束),
OtherEnd=获取源连接(套接字),
?模块:接受的_连接(另一端),
回路(插座,另一端);
{错误,{}=原因->
?错误([“侦听器无法接受连接”,
{listener,self()},{reason,reason}])
结束。
%%%-------------------------------------------------------------------------
%%功能:循环/2
%%紫色