Erlang服务器与端口连接,用于向Java应用程序发送和接收Json文件

Erlang服务器与端口连接,用于向Java应用程序发送和接收Json文件,erlang,erlang-ports,Erlang,Erlang Ports,我尝试在Java应用程序中实现一个带有Erlang的服务器。 似乎我的服务器正在工作,但仍然充满了bug和死点。 我需要接收一个由Java应用程序解析成映射的JSON文件,并将其发送回所有客户端,包括上传该文件的客户端。 同时,我需要跟踪谁发出了请求以及发送了消息的哪一部分,如果出现任何问题,客户端应该从这一点重新启动,而不是从一开始。除非客户端离开应用程序,否则它应该重新启动 下面是我的三段代码: app.erl supervisor.erl: 这是我做的第一个服务器,我可能在中间丢失了。当我

我尝试在Java应用程序中实现一个带有Erlang的服务器。 似乎我的服务器正在工作,但仍然充满了bug和死点。 我需要接收一个由Java应用程序解析成映射的JSON文件,并将其发送回所有客户端,包括上传该文件的客户端。 同时,我需要跟踪谁发出了请求以及发送了消息的哪一部分,如果出现任何问题,客户端应该从这一点重新启动,而不是从一开始。除非客户端离开应用程序,否则它应该重新启动

下面是我的三段代码:

app.erl supervisor.erl: 这是我做的第一个服务器,我可能在中间丢失了。当我跑步的时候,它似乎在工作,但是在悬挂。有人能帮我吗?我的本地主机从不加载它一直加载的任何内容

我的java应用程序如何从同一端口接收它

我必须使用Erlang和端口来连接java应用程序


谢谢你帮助我

让我们稍微修改一下

首先:命名

我们在Erlang中不使用camelCase。这是令人困惑的,因为大写变量名和小写(或单引号)原子的含义不同。此外,模块名必须与文件名相同,这会导致不区分大小写的文件系统出现问题

此外,我们真的想要一个比“服务器”更好的名称。在这样的系统中,服务器可能意味着很多事情,虽然整个系统可能是用Erlang编写的服务,但这并不一定意味着我们可以调用“服务器”中的所有内容而不会变得非常模糊!那太令人困惑了。现在我将把你的项目命名为“ES”。所以你会有
es_app
es_sup
等等。当我们想开始定义新的模块时,这将派上用场,其中一些模块可能被称为“服务器”,而不必到处写“服务器”

第二:输入数据

一般来说,我们希望将参数传递给函数,而不是将文字(或者更糟糕的是,宏重写)隐藏在代码中。如果我们想要有神奇的数字和常量,让我们尽最大努力将它们放入配置文件中,这样我们就可以,甚至更好,让我们在初始启动调用中将它们用作从属进程的参数,这样我们就可以修改系统的行为(一旦编写)只需在主应用程序模块中处理启动调用函数

-module(es).
-behaviour(application).

-export([listen/1, ignore/0]).
-export([start/0, start/1]).
-export([start/2, stop/1]).

listen(PortNum) ->
    es_client_man:listen(PortNum).

ignore() ->
    es_client_man:ignore().

start() ->
    ok = application:ensure_started(sasl),
    ok = application:start(?MODULE),
    io:format("Starting es...").


start(Port) ->
    ok = start(),
    ok = es_client_man:listen(Port),
    io:format("Startup complete, listening on ~w~n", [Port]).

start(normal, _Args) ->
    es_sup:start_link().

stop(_State) ->
    ok.
我在上面添加了一个start/1函数,以及一个start/0、一个listen/1和一个ignore/0,稍后您将在es_client_man中再次看到这些函数。这些都是方便的包装,可以更明确地调用,但可能不想一直键入

此应用程序模块首先让应用程序主控程序为我们启动项目(通过调用
application:start/1
),然后下一行调用erl_server_server让它开始侦听。在早期的开发中,我发现这种方法比将AutoStart埋在每个组件上更有用,后来它为我们提供了一种非常简单的方法来编写一个可以打开和关闭各种组件的外部接口

啊,还有。。。我们将把它作为一个真正的Erlang应用程序启动,因此我们需要
ebin/
中的app文件(或者如果您使用的是Erlang.mk或类似的东西,
src/
中的app.src文件):

ebin/es.app如下所示:

{application,es,
             [{description,"An Erlang Server example project"},
              {vsn,"0.1.0"},
              {applications,[stdlib,kernel,sasl]},
              {modules,[es,
                        es_sup,
                          es_clients,
                            es_client_sup,
                              es_client,
                            es_client_man]},
              {mod,{es,[]}}]}.
modules
下的列表实际上反映了监控树的布局,您将在下面看到

上面的start/2函数现在断言我们将只在
正常模式下启动(这可能合适,也可能不合适),并忽略启动参数(也可能合适,也可能不合适)

第三:

然后

-module(es_clients).
-behavior(supervisor).

-export([start_link/0]).
-export([init/1]).

start_link() ->
  supervisor:start_link({local, ?MODULE}, ?MODULE, none).

init(none) ->
    RestartStrategy = {rest_for_one, 1, 60},
    ClientSup = {es_client_sup,
                 {es_client_sup, start_link, []},
                 permanent,
                 5000,
                 supervisor,
                 [es_client_sup]},
    ClientMan = {es_client_man,
                 {es_client_man, start_link, []},
                 permanent,
                 5000,
                 worker,
                 [es_client_man]},
    Children  = [ClientSup, ClientMan],
    {ok, {RestartStrategy, Children}}.
哇!这是怎么回事?!?嗯,主管是一个主管,而不是一个一次性的东西,只会产生其他一次性的东西。(误解是你核心问题的一部分。)

主管很无聊。主管应该很无聊。作为代码的读者,他们真正要做的就是监督树的结构是什么。他们在OTP结构方面为我们做了什么是非常重要的,但他们不要求我们编写任何程序代码,只需要声明它应该有哪些子级。我们在这里实现的称为结构。因此,我们为您的整个应用程序配备了名为
es\u sup
的顶级主管。下面是(目前)一个名为
es\u clients
的单一服务组件

es_客户流程也是一名主管。这样做的原因是为了定义一种明确的方式,使客户端连接部分不会影响以后系统其余部分中可能存在的任何正在进行的状态。仅仅接受来自客户机的连接是没有用的——当然,在其他地方存在一些重要的状态,比如到某个Java节点的长时间运行的连接或其他什么。这将是一个单独的服务组件,可能称为
es_java_nodes
,程序的这一部分将从它自己的、单独的主管开始。这就是为什么它被称为“监督树”而不是“监督列表”

所以回到客户那里。。。我们将有客户连接。这就是为什么我们称它们为“客户机”,因为从这个Erlang系统的角度来看,连接的是客户机,而接受这些连接的进程抽象了客户机,因此我们可以将每个连接处理程序视为客户机本身——因为这正是它所代表的。如果我们稍后连接到上游服务,我们希望调用这些服务,无论它们抽象什么,以便我们在系统中的语义是合理的

然后,您可以考虑“一个es_客户端向一个es_java_节点发送一条消息来查询[thingy]”,而不是像“一个服务器问一个jav”那样试图保持事情的直截了当
-module(es).
-behaviour(application).

-export([listen/1, ignore/0]).
-export([start/0, start/1]).
-export([start/2, stop/1]).

listen(PortNum) ->
    es_client_man:listen(PortNum).

ignore() ->
    es_client_man:ignore().

start() ->
    ok = application:ensure_started(sasl),
    ok = application:start(?MODULE),
    io:format("Starting es...").


start(Port) ->
    ok = start(),
    ok = es_client_man:listen(Port),
    io:format("Startup complete, listening on ~w~n", [Port]).

start(normal, _Args) ->
    es_sup:start_link().

stop(_State) ->
    ok.
{application,es,
             [{description,"An Erlang Server example project"},
              {vsn,"0.1.0"},
              {applications,[stdlib,kernel,sasl]},
              {modules,[es,
                        es_sup,
                          es_clients,
                            es_client_sup,
                              es_client,
                            es_client_man]},
              {mod,{es,[]}}]}.
-module(es_sup).
-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

start_link() ->
  supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    RestartStrategy = {one_for_one, 1, 60},
    Clients   = {es_clients,
                 {es_clients, start_link, []},
                 permanent,
                 5000,
                 supervisor,
                 [es_clients]},
    Children  = [Clients],
    {ok, {RestartStrategy, Children}}.
-module(es_clients).
-behavior(supervisor).

-export([start_link/0]).
-export([init/1]).

start_link() ->
  supervisor:start_link({local, ?MODULE}, ?MODULE, none).

init(none) ->
    RestartStrategy = {rest_for_one, 1, 60},
    ClientSup = {es_client_sup,
                 {es_client_sup, start_link, []},
                 permanent,
                 5000,
                 supervisor,
                 [es_client_sup]},
    ClientMan = {es_client_man,
                 {es_client_man, start_link, []},
                 permanent,
                 5000,
                 worker,
                 [es_client_man]},
    Children  = [ClientSup, ClientMan],
    {ok, {RestartStrategy, Children}}.
-module(es_client_sup).
-behaviour(supervisor).

-export([start_acceptor/1]).
-export([start_link/0]).
-export([init/1]).

start_acceptor(ListenSocket) ->
    supervisor:start_child(?MODULE, [ListenSocket]).

start_link() ->
  supervisor:start_link({local, ?MODULE}, ?MODULE, none).

init(none) ->
    RestartStrategy = {simple_one_for_one, 1, 60},
    Client    = {es_client,
                 {es_client, start_link, []},
                 temporary,
                 brutal_kill,
                 worker,
                 [es_client]},
    {ok, {RestartStrategy, [Client]}}.
-module(es_client_man).
-behavior(gen_server).

-export([listen/1, ignore/0]).
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         code_change/3, terminate/2]).

-record(s, {port_num = none :: none | inet:port_number(),
            listener = none :: none | gen_tcp:socket()}).

listen(PortNum) ->
    gen_server:call(?MODULE, {listen, PortNum}).

ignore() ->
    gen_server:cast(?MODULE, ignore).

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, none, []).

init(none) ->
    ok = io:format("Starting.~n"),
    State = #s{},
    {ok, State}.

handle_call({listen, PortNum}, _, State) ->
    {Response, NewState} = do_listen(PortNum, State),
    {reply, Response, NewState};
handle_call(Unexpected, From, State) ->
    ok = io:format("~p Unexpected call from ~tp: ~tp~n", [self(), From, Unexpected]),
    {noreply, State}.

handle_cast(ignore, State) ->
    NewState = do_ignore(State),
    {noreply, NewState};
handle_cast(Unexpected, State) ->
    ok = io:format("~p Unexpected cast: ~tp~n", [self(), Unexpected]),
    {noreply, State}.

handle_info(Unexpected, State) ->
    ok = io:format("~p Unexpected info: ~tp~n", [self(), Unexpected]),
    {noreply, State}.

code_change(_, State, _) ->
    {ok, State}.

terminate(_, _) ->
    ok.

do_listen(PortNum, State = #s{port_num = none}) ->
    SocketOptions =
        [{active,    once},
         {mode,      binary},
         {keepalive, true},
         {reuseaddr, true}],
    {ok, Listener} = gen_tcp:listen(PortNum, SocketOptions),
    {ok, _} = es_client:start(Listener),
    {ok, State#s{port_num = PortNum, listener = Listener}};
do_listen(_, State = #s{port_num = PortNum}) ->
    ok = io:format("~p Already listening on ~p~n", [self(), PortNum]),
    {{error, {listening, PortNum}}, State}.

do_ignore(State = #s{listener = none}) ->
    State;
do_ignore(State = #s{listener = Listener}) ->
    ok = gen_tcp:close(Listener),
    State#s{listener = none}.
-module(es_client).

-export([start/1]).
-export([start_link/1, init/2]).
-export([system_continue/3, system_terminate/4,
         system_get_state/1, system_replace_state/2]).

-record(s, {socket = none :: none | gen_tcp:socket()}).

start(ListenSocket) ->
    es_client_sup:start_acceptor(ListenSocket).

start_link(ListenSocket) ->
    proc_lib:start_link(?MODULE, init, [self(), ListenSocket]).

init(Parent, ListenSocket) ->
    ok = io:format("~p Listening.~n", [self()]),
    Debug = sys:debug_options([]),
    ok = proc_lib:init_ack(Parent, {ok, self()}),
    listen(Parent, Debug, ListenSocket).

listen(Parent, Debug, ListenSocket) ->
    case gen_tcp:accept(ListenSocket) of
        {ok, Socket} ->
            {ok, _} = start(ListenSocket),
            {ok, Peer} = inet:peername(Socket),
            ok = io:format("~p Connection accepted from: ~p~n", [self(), Peer]),
            State = #s{socket = Socket},
            loop(Parent, Debug, State);
        {error, closed} ->
            ok = io:format("~p Retiring: Listen socket closed.~n", [self()]),
            exit(normal)
     end.

loop(Parent, Debug, State = #s{socket = Socket}) ->
    ok = inet:setopts(Socket, [{active, once}]),
    receive
        {tcp, Socket, <<"bye\r\n">>} ->
            ok = io:format("~p Client saying goodbye. Bye!~n", [self()]),
            ok = gen_tcp:send(Socket, "Bye!\r\n"),
            ok = gen_tcp:shutdown(Socket, read_write),
            exit(normal);
        {tcp, Socket, Message} ->
            ok = io:format("~p received: ~tp~n", [self(), Message]),
            ok = gen_tcp:send(Socket, ["You sent: ", Message]),
            loop(Parent, Debug, State);
        {tcp_closed, Socket} ->
            ok = io:format("~p Socket closed, retiring.~n", [self()]),
            exit(normal);
        {system, From, Request} ->
            sys:handle_system_msg(Request, From, Parent, ?MODULE, Debug, State);
        Unexpected ->
            ok = io:format("~p Unexpected message: ~tp", [self(), Unexpected]),
            loop(Parent, Debug, State)
    end.

system_continue(Parent, Debug, State) ->
    loop(Parent, Debug, State).

system_terminate(Reason, _Parent, _Debug, _State) ->
    exit(Reason).

system_get_state(Misc) -> {ok, Misc}.

system_replace_state(StateFun, Misc) ->
    {ok, StateFun(Misc), Misc}.
es/
   Emakefile
   ebin/es.app
   src/*.erl
1> code:add_patha("ebin").
true
2> make:all().
up_to_date
3> es:start().
4> es:listen(5555).
<0.94.0> Listening.
ok
ceverett@changa:~/vcs/es$ telnet localhost 5555
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello es thingy
You sent: Hello es thingy
Yay! It works!
You sent: Yay! It works!
bye
Bye!
Connection closed by foreign host.
<0.96.0> Listening.
<0.94.0> Connection accepted from: {{127,0,0,1},60775}
<0.94.0> received: <<"Hello es thingy\r\n">>
<0.94.0> received: <<"Yay! It works!\r\n">>
<0.94.0> Client saying goodbye. Bye!
<0.97.0> Listening.
<0.96.0> Connection accepted from: {{127,0,0,1},60779}
<0.98.0> Listening.
<0.97.0> Connection accepted from: {{127,0,0,1},60780}
<0.97.0> received: <<"Screen 1\r\n">>
<0.96.0> received: <<"Screen 2\r\n">>
<0.97.0> received: <<"I wonder if I could make screen 1 talk to screen 2...\r\n">>
<0.96.0> received: <<"Time to go\r\n">>
<0.96.0> Client saying goodbye. Bye!
<0.97.0> Client saying goodbye. Bye!