Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/apache/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Erlang gen_服务器正在关闭侦听套接字_Erlang_Gen Server_Gen Tcp - Fatal编程技术网

Erlang 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

我试图做的是让gen_服务器进程接受一个新的客户端,并立即生成一个新的子进程来处理下一个客户端。我看到的问题是,当套接字完成并最终终止时,它也会关闭侦听套接字,我不知道为什么,即使它不再引用它

知道我做错了什么吗

gen_服务器:

-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 %%紫色