如何使Erlang客户端/服务器与配置中的参数/节点连接?

如何使Erlang客户端/服务器与配置中的参数/节点连接?,erlang,client-server,nodes,distributed,Erlang,Client Server,Nodes,Distributed,我正在用Erlang编写客户机/服务器系统,无法让它们连接。它们应该使用的节点存储在配置文件中,这些文件在启动它们时加载。它们还使用几种分布式数据结构。问题是,我首先启动数据结构,然后服务器(工作正常),但当我启动客户机时,它不连接任何东西,并引发异常 服务器: -module(server). -export([startServer/0,loopServer/7,node/0,log/0,name/0]). -compile({no_auto_import,[node/0]}). -imp

我正在用Erlang编写客户机/服务器系统,无法让它们连接。它们应该使用的节点存储在配置文件中,这些文件在启动它们时加载。它们还使用几种分布式数据结构。问题是,我首先启动数据结构,然后服务器(工作正常),但当我启动客户机时,它不连接任何东西,并引发异常

服务器:

-module(server).

-export([startServer/0,loopServer/7,node/0,log/0,name/0]).
-compile({no_auto_import,[node/0]}).
-import(werkzeug, [get_config_value/2,lengthSL/1,logging/2,reset_timer/3,get_config_value/2]).
-import(erlang, [append/2]).

log() ->
    erlang:list_to_atom(lists:concat(["Server@", node(),".log"])).

node() ->
    {ok,Config} = file:consult("configs/server.cfg"), 
    {ok, Node} = werkzeug:get_config_value(node,Config),
    Node.

name() ->
    {ok,Config} = file:consult("configs/server.cfg"), 
    {ok, Name} = werkzeug:get_config_value(servername,Config),
    Name.


%%Startet den Server und die darin enthaltene Schleife
startServer() -> 
    {ok,Config} = file:consult("configs/server.cfg"), 
    {ok, Name} = get_config_value(servername,Config),

    %%CMEM initialisieren
    {ok, RemTime} = get_config_value(cmemtime,Config),
    CMEM = cmem:initCMEM(RemTime, log()),

    %%HBQ-Prozess erzeugen
    {ok,HBQName} = get_config_value(hbqname,Config),
    {ok,HBQNode} = get_config_value(node,Config),
    HBQ = {HBQName,HBQNode},

    {ok,Serverzeit} = get_config_value(serverzeit,Config),
    %%Zeitpunkt des Timer übergeben
    {ok, Timer} = timer:send_after(round(RemTime * 1000),Name,delServer),

    %%Prozess registrieren, Serverschleife mit allen Infos starten, plus NNr 1
    ServerPid = spawn(?MODULE, loopServer, [Name,CMEM,HBQ,Timer,Serverzeit,Config,1]),                                                          
    register(Name,ServerPid),

    %%HBQ initialisieren
    HBQ ! {ServerPid, {request,initHBQ}},

    {Config,CMEM,HBQ,ServerPid}.


loopServer(Name,CMEM,HBQ,Timer,Serverzeit,Config,NNr) ->
    receive
        %%Client fragt neue Nachrichten ab, dazu wird aus CMEM die aktuelle NNr für 
        %%den Client angefordert und mit der ClientPID an die HBQ weitergegeben
        {ClientPID,getmessages} ->
            NewTimer = reset_timer(Timer,Serverzeit,delServer),
            ClientNNr = cmem:getClientNNr(CMEM, ClientPID),
            HBQ ! {self(), {request, deliverMSG, ClientNNr, ClientPID}},
            logging(log(), lists:concat(["Server: Nachricht ", NNr, " wurde zugestellt.\n"])),

            loopServer(Name,CMEM,HBQ,NewTimer,Serverzeit,Config,NNr);

        %%Nachricht soll in HBQ verschoben werden
        {dropmessage,[INNr,Msg,TSclientout]} ->
            NewTimer = reset_timer(Timer,Serverzeit,delServer),
            HBQ ! {self(), {request,pushHBQ,[INNr,Msg,TSclientout]}},
            receive
                {reply,ok} ->
                logging(log(), lists:concat(["Server: Nachricht ", INNr, " wurde in HBQ eingefuegt.\n"]))
            end,
            loopServer(Name,CMEM,HBQ,NewTimer,Serverzeit,Config,NNr);

        %%Client fragt naechste NNr ab, diese wird dem Zustand des Server entnommen
        {ClientPID,getmsgid} ->
          NewTimer = reset_timer(Timer,Serverzeit,delServer),
          ClientPID ! {nid, NNr},
          NewNNr = NNr + 1,
          logging(log(), lists:concat(["Server: Nachrichtennumer ", NNr, " an ", erlang:pid_to_list(ClientPID), "gesendet.\n"])),
          loopServer(Name,CMEM,HBQ,NewTimer,Serverzeit,Config,NewNNr);

        %%Server beendet sich selbst und zugleich die HBQ
        delServer ->
            HBQ ! {self(),{request,delHBQ}},
            receive
                {reply,ok} ->
                logging(log(), lists:concat([lists:concat(["Server: Downtime ", werkzeug:timeMilliSecond(), " von ", name() ,"\n"])])),
                ok
            end
    end.
客户:

-module(client).

-export([startClients/0,loopClient/4,spawnC/1,forLoop/3,mitServerVerbinden/6,configLaden/0]).

-import(werkzeug, [get_config_value/2]).
-import(timer, [apply_after/4]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   Client  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%initClient
startClients() ->
  Config = configLaden(),
  {ok, ClientAnzahl} = werkzeug:get_config_value(clientanzahl, Config),
  {ok, ServerName} = werkzeug:get_config_value(servername, Config),
  {ok, ServerNode} = werkzeug:get_config_value(servernode, Config),
  ServerPid = {ServerName,ServerNode},
forLoop(ClientAnzahl, fun client:spawnC/1, ServerPid).


%%Hilfsfunktion fuer for-Schleife: Zaehlt runter,
%%ruft Funktion auf und gibt Ergebnisse als Liste zurueck
forLoop(Clients, Spawn, SPid) -> forLoop(Clients, Spawn, SPid, []).
forLoop(0, _Spawn, _SPid, ClientListe) -> ClientListe;
forLoop(Clients, Spawn, SPid, ClientListe) ->
  ClientListeNew = ClientListe ++ [Spawn(SPid)],
  ClientsNew = Clients - 1,
forLoop(ClientsNew, Spawn, SPid, ClientListeNew).

%%Neuen ClientProzess erzeugen
spawnC(ServerPid) ->
  Config = configLaden(),
  {ok, Lebenszeit} = werkzeug:get_config_value(lebenszeit, Config),
  {ok, Cookie} = werkzeug:get_config_value(cookie, Config),
  {ok, ServerNode} = werkzeug:get_config_value(servernode, Config),
  {ok, Wartezeit} = werkzeug:get_config_value(wartezeit, Config),
  ClientPid = erlang:spawn(?MODULE, mitServerVerbinden, [ServerPid, [], [], Wartezeit, ServerNode, Cookie]),
  timer:kill_after(Lebenszeit, ClientPid),
ClientPid.

%%mit Server Verbinden
mitServerVerbinden(ServerPid,Datei,NNummern,Wartezeit,ServerNode,Cookie) ->
  erlang:set_cookie(ServerNode,Cookie),
  pong = net_adm:ping(ServerNode),
loopClient(ServerPid,NNummern,Wartezeit,Datei).



%%loopClient
loopClient(ServerPid,NNummern,Wartezeit,Datei) ->

  %%Client wird zum Redakteur
  {NNummernNew, WartezeitNew, DateiNew} = nachrichtenSenden(5,ServerPid,NNummern,Wartezeit,Datei),

  %%Client wird zum Leser
  nachrichtenLesen(false, NNummernNew, ServerPid,DateiNew),

  %%Methode ruft sich selbst wieder auf
  loopClient(ServerPid, NNummernNew, WartezeitNew,DateiNew).


configLaden() ->
  {ok, Config} = file:consult("configs/client.cfg"),
  Config. 




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Redakteur %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%Nachricht soll vergessen werden
nachrichtenSenden(0,ServerPid,NNummern,Wartezeit,Datei) ->

  %%Naechste NNr beim Server anfragen
  ServerPid ! {self(),getmsgid},
  receive 

    %%Server sendet NNr
    {nid,NNr} ->

      %%Logeintrag
  werkzeug:logging(Datei,lists:concat([NNr, "te Nachricht um ", werkzeug:timeMilliSecond(), " vergessen zu senden. ******\n"]))
    end,
  WartezeitNew = wartezeitBerechnen(Wartezeit),

  %%Rückgabewerte: Liste mit Nachrichtennummern fuer leser, neue Wartezeit, Logfile
  {NNummern,WartezeitNew,Datei};




%%Nachrichtennummer anfragen und erhalten, Nachricht schreiben
nachrichtenSenden(NachrichtenVorVergessen,ServerPid,NNummern,Wartezeit,Datei) ->
  Config = configLaden(),
  {ok, ServerNode} = werkzeug:get_config_value(servernode, Config),

  %%Naechste NNr beim Server anfragen
  ServerPid ! {self(),getmsgid},
  receive

    %%Server sendet NNr
    {nid,NNr} ->

     %%Nachricht schreiben
     Nachricht = nachrichtSchreiben(NNr),

      %%NNr zur Liste hinzufuegen fuer Leser
      NNummernNew = NNummern ++[NNr],
      timer:sleep(Wartezeit),

      %%Nachricht an Server senden
      ServerPid ! {dropmessage,[NNr,Nachricht,erlang:now()]},

      %%Logeintrag schreiben
      werkzeug:logging(Datei,lists:concat([Nachricht, " gesendet"])),

    %%Neue Wartezeit berechnen  
    WartezeitNew = wartezeitBerechnen(Wartezeit),

    %%Zaehler dekrementieren  
    NachrichtenVorVergessenNew = NachrichtenVorVergessen -1,

  %%Methode neu aufrufen
  nachrichtenSenden(ServerPid,NNummernNew,WartezeitNew,NachrichtenVorVergessenNew,Datei)
  end.



%%nachrichtSchreiben
nachrichtSchreiben(NNr) ->
  Config = configLaden(),
  {ok, Rechner} = werkzeug:get_config_value(rechner, Config),
  {ok, Praktikumsgruppe} = werkzeug:get_config_value(praktikumsgruppe, Config),
  {ok, Teamnummer} = werkzeug:get_config_value(teamnummer, Config),
lists:concat(["client@",Rechner, "_", Praktikumsgruppe, "_", Teamnummer, ": ", integer_to_list(NNr), "_te Nachricht. Sendezeit: ", werkzeug:timeMilliSecond()]).


%%Hilfsmethode: Intervall darf nicht kleiner als zwei Sekunden werden
minimumTime() -> 2000.

%%Berechnet das neue Zeitintervall, um die Haelfte groesser oder
%%kleiner als die alte Zeit, solange sie groesser gleich 2 Sekunden ist
wartezeitBerechnen(Wartezeit) -> 
    GroesserKleiner = werkzeug:bool_rand(),
  HaelfteZeit = trunc(max(Wartezeit * 0.5, minimumTime())),
  if
    GroesserKleiner ->
      Wartezeit + HaelfteZeit;
    true ->
      max(Wartezeit - HaelfteZeit, minimumTime())
end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    Leser   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


nachrichtenLesen(true,NNummern,ServerPid,Datei) -> ok;

nachrichtenLesen(false,NNummern,ServerPid,Datei) ->
  ServerPid ! {self(),getmessages},
  receive 
    {reply,Message,Terminated} ->
    nachrichtInLogSchreiben(Message,NNummern,Datei),
    nachrichtenLesen(Terminated,NNummern,ServerPid,Datei)
  end.

nachrichtInLogSchreiben([NNr,Msg,TSclientout,TShbqin,TSdlqin,TSdlqout],NNummern,Datei) ->
  Now = erlang:timestamp(),
  DLQInTrue = werkzeug:validTS(TSdlqin),
  DLQOutTrue = werkzeug:validTS(TSdlqout),
  DLQInZukunft = werkzeug:lessTS(Now, TSdlqin),
  DLQOutZukunft = werkzeug:lessTS(Now, TSdlqout),
  MsgVonGleichemClient = msgVonGleichemClient(NNr, Msg, NNummern),
  if
      DLQInTrue and DLQInZukunft ->
      Zeitunterschied = werkzeug:now2stringD(werkzeug:diffTS(TSdlqin, Now)),
      MsgNew = MsgVonGleichemClient ++ ", Zeitunterschied: " ++ Zeitunterschied,
      werkzeug:logging(Datei, MsgNew ++ "\n");
    DLQOutTrue and DLQOutZukunft ->
      Zeitunterschied = werkzeug:now2stringD(werkzeug:diffTS(TSdlqout, Now)),
      MsgNew = MsgVonGleichemClient ++ ", Zeitunterschied: " ++ Zeitunterschied,
      werkzeug:logging(Datei, MsgNew ++ "\n");
    true ->
      werkzeug:logging(Datei, MsgVonGleichemClient ++ "\n")
  end.


msgVonGleichemClient(NNr,Msg,NNummern) ->
    MsgVonGleichemClient = lists:member(NNr, NNummern),
  if
    MsgVonGleichemClient -> Msg ++ "*******";
    true -> Msg
end.
服务器的配置文件:

{sizedlq,400}.
{servername,'serverpa'}.
{cmemtime,2000}.
{hbqname,'hbqpa'}.
{node,'hbqnode'}.
{serverzeit,50}.
客户端的配置文件:

{wartezeit,20}.
{lebenszeit,240}.
{pratikumsgruppe,'2'}.
{teamnummer,'02'}.
{servername,'serverpa'}.
{clientanzahl,5}.
{servernode,'servernode'}.
{cookie,pa}.
{rechner,'rechner'}.
还有一些分布式数据结构,它们本质上是队列,似乎运行良好:

哈佛商学院:

客户机应该联系服务器向其发送消息,服务器将其放入数据结构中,在适当的情况下将其发送回客户机。 问题是,当我编译它们时,首先启动HBQ,然后是服务器,然后是客户端,我得到

=ERROR REPORT==== 18-Apr-2017::10:25:46 ===
Error in process <0.104.0> with exit value: {distribution_not_started,[{auth,set_cookie,2,[{file,"auth.erl"},{line,119}]},{client,mitServerVerbinden,6,[{file,"client.erl"},{line,42}]}]}
=错误报告===2017年4月18日::10:25:46===
使用退出值处理时出错:{distribution_not_start,[{auth,set_cookie,2,[{file,“auth.erl”},{line,119}]},{client,mitServerVerbinden,6,[{file,“client.erl”},{line,42}}
因此,显然客户端没有连接到服务器存在问题。这是我第一次使用Erlang和分布式系统,所以我不知道发生了什么


将节点和cookie放入配置中并告诉系统的组件查看是否还不够?

当调用它的erlang VM没有名称时,auth模块返回错误
分发未启动。确保启动erlang时传递了
-sname
-name
标志,例如:

erl -sname test
-module(cmem).

-export([initCMEM/2, delCMEM/1, updateClient/4, getClientNNr/2]).

-import(werkzeug, [get_config_value/2,lengthSL/1,logging/2,reset_timer/3,get_config_value/2,getUTC/0]).

%%Initialisiert CMEM
initCMEM(RemTime, Datei) -> werkzeug:logging(Datei, lists:concat(["CMEM>>> initialisiert mit  ", RemTime, " Sekunden\n"])),
  [[], RemTime].

%%Loescht CMEM
delCMEM(_CMEM) -> ok.

%%Speichert/aktualisiert Client und NNr im CMEM, 
updateClient([CMEM, RemTime], ClientID, NNr, Datei) -> 
  ClientTS = getUTC(),
  logging(Datei, lists:concat(["CMEM>>> Client ", ClientID, " wird aktualisiert.\n"])),
  [lists:keystore(ClientID, 1, CMEM, {ClientID, NNr, ClientTS}), RemTime].

%%Gibt naechste vom Client erwartete NNr zurueck
%%Es wird geprueft ob Client in der Liste steht und dann
%%mit diesem Wissen eine Hilfsfunktion aufgerufen
getClientNNr([CMEM, RemTime], ClientID) ->
  ClientBekannt = lists:keymember(ClientID, 1, CMEM),
  isClientKnown([CMEM, RemTime], ClientID, ClientBekannt).

%%Client ist nicht bekannt: Naechste NNr = 1
isClientKnown(_CMEM, _ClientID, false) -> 1;

%% Der Client ist bekannt.
%%Zeit noch nicht abgelaufen: Gibt naechste Nummer aus
%%Zeit abgelaufen: Naechste NNr = 1
isClientKnown([CMEM, RemTime], ClientID, true) ->
  {ClientID, NNr, ClientTS} = lists:keyfind(ClientID, 1, CMEM),
  RemainingTime = ClientTS + RemTime,
  Now = getUTC(),
  if
    RemainingTime >= Now -> NNr + 1;
    true -> 1
end.
=ERROR REPORT==== 18-Apr-2017::10:25:46 ===
Error in process <0.104.0> with exit value: {distribution_not_started,[{auth,set_cookie,2,[{file,"auth.erl"},{line,119}]},{client,mitServerVerbinden,6,[{file,"client.erl"},{line,42}]}]}
erl -sname test