我是Erlang的新手,但是我一直在尝试创建一个简单的客户端服务器实现。该服务器将假设创建工作流程以在"数据库"中进行"重工作",然后将计算值返回给客户端。
我当前的步骤是:
- 创建服务器过程。
- 产生工人。
- 向工人发送一些工作以基于客户的输入。(这是我困惑的地方(
- 将数据从工人发送回客户。
这是一些示例代码。
-module(server).
-compile(export_all).
server() ->
receive
{From, {client, Name}} ->
io:format("Server has received request for ~p from ~p~n", [Name, From]),
Worker = spawn(server, getNameFromDataBase(self()),[]),
Worker ! Name,
From ! LastName,%%data returned from worker
server();
{database, LastName} ->
Data = LastName,
server()
end.
getNameFromDataBase(Server_Address) ->
receive
{name, Name} ->
timer:sleep(5000), %doing difficult work!
Server_Address ! {database, "Johnson"}
end.
client(Server_Address) ->
Server_Address ! {self(), {client, "Jim"}},
receive
{server, LastName} ->
io:format("Server got person's last name~p~n", LastName)
end.
您如何做到这一点,以便看到从工人那里收到的数据并可以将其发送给客户?
我可以看到您在服务器过程中有两个主要问题:
- 将消息从服务器发送到数据库工人。
- 将服务器从工人收到的消息发送回客户端。
将消息发送给工人
您从L8 -L10中的以下代码行不正确。
%% ...
Worker = spawn(server, getNameFromDataBase(self()), []),
Worker ! Name,
From ! LastName,
%% ...
要产生一个函数作为一个过程,您必须在spawn/3
的第三个参数中作为列表成员提供任何参数,而不是直接调用该函数。
Worker = spawn(server, getNameFromDataBase, [self()]),
由于getNameFromDataBase
期望{name, Name}
,仅将Name
发送到Worker
将不会被注意到。这应该更改为
Worker ! {name, Name}
发送结果(lastName(到客户端
在L10上发送From ! LastName
是没有意义的,因为您尚未收到LastName
。当服务器从工人接收{database, LastName}
时,应将其移至第二个匹配表达式。此外,client
期望{server, LastName}
,而不是LastName
。因此应该是From ! {server, LastName}
。
但是,有一个问题。您无法在该范围中访问From
,因为它从未定义。
%% ...
{database, LastName} ->
From ! {server, LastName}, % `From` is not defined
server()
%% ...
您可能想做的而不必更改消息元组是定义另一个server/1
函数,例如:
server(ClientPID) ->
receive
{_, LastName} ->
ClientPID ! {server, LastName},
server()
end.
并从server/0
致电:
server() ->
receive
{From, {client, Name}} ->
io:format("Server has received request for ~p from ~p~n", [Name, From]),
Worker = spawn(?MODULE, getNameFromDataBase, [self()]),
Worker ! {name, Name},
server(From) % call `server/1` with the client's PID
end.
最后,要包装所有内容,如果您还没有,请创建一个启动功能来产生server
和client
进程。这是完整的代码:
server() ->
receive
{From, {client, Name}} ->
io:format("Server has received request for ~p from ~p~n", [Name, From]),
Worker = spawn(?MODULE, getNameFromDataBase, [self()]),
Worker ! {name, Name},
server(From)
end.
server(ClientPID) ->
receive
{_, LastName} ->
ClientPID ! {server, LastName},
server()
end.
getNameFromDataBase(ServerAddr) ->
receive
{name, Name} ->
io:format("worker received ~p~n",[Name]),
timer:sleep(5000),
ServerAddr ! {database, "Johnson"}
end.
client(ServerPID) ->
ServerPID ! {self(),{client, "Jim"}},
receive
{server, LastName} ->
io:format("Client got person's last name ~p from Server ~n", [LastName])
end.
run() ->
ServerPID = spawn(?MODULE, server, []),
spawn(?MODULE, client, [ServerPID]).
最后提示:请勿使用export_all
编译选项。仅导出必要的功能以实现良好的封装。
您将从接收的{database, LastName} ->
分支中的工人那里收到消息。只需在消息本身中包括您需要处理的信息:
server() ->
receive
{From, {client, Name}} ->
io:format("Server has received request for ~p from ~p~n", [Name, From]),
Worker = spawn(server, getNameFromDataBase, [self()]),
Worker ! {name, From, Name},
server();
{database, From, LastName} ->
From ! LastName,
server()
end.
getNameFromDataBase(Server_Address) ->
receive
{name, From, Name} ->
timer:sleep(5000), %doing difficult work!
Server_Address ! {database, From, "Johnson"}
end.
请注意,该参数需要在列表中传递(spawn
的第三个参数(。另外,如果您创建工人只是要立即发送一条消息,那么您也可以将其内容作为参数传递;我没有在这里进行此更改,以使看到主要更改变得更容易。
另外,您可以在服务器状态中存储与Worker
相对应的From
(参数到server()
(。