我在学习二郎的早期阶段,我需要一些进一步的帮助
- 看一些简单的,非gen_server客户端 - 服务器的例子。尝试为您自己的客户端服务器想出一个简单的想法并编写代码。
- 了解有关使用模块名称参数化简单服务器的信息。
- 了解gen_server和行为。
- 练习将简单的服务器转换为gen_server。使用带分割窗口的文本编辑器非常方便。
- 了解gen_tcp和套接字。
- 查看将gen_tcp和套接字与gen_server结合使用的示例。
在这里看到:
http://erlang.org/doc/design_principles/des_princ.html
我不会在第6步,你似乎在做开始。
这意味着,任何TCP数据自动地被 gen_server处理:handle_info/2 - 它获取一个呼叫切换到 MODULE:handle_info?
没有回调。 TCP数据绕过整个gen_server架构。可以说,TCP数据与其他入侵者一起进入后门。所以gen_server:handle_info()在那里处理它们。 handle_info()检查服务器邮箱中是否有与指定为handle_info()参数的模式相匹配的消息。
任何需要完成的TCP数据都是在handle_info()内部完成的。当然,如果你需要做一些复杂的数据处理handle_info(),你可以随时调用辅助函数来计算的一些结果:
handle_info({tcp, Socket, RawData}, State) ->
Result1 = computerInGermanyProcess(RawData),
Result2 = computerInFranceProcess(RawData),
Result3 = computerInRussiaProcess(RawData),
Result4 = State#state.stored_val,
gen_tcp:send(Socket, [Result1, Result2, Result3, Result4]),
{noreply, State}; %%Because a TCP message landed in the mailbox with no From value,
%%do not reply to From, and do not mess with the value in State.
computerInGermanyProcess(RawData) ->
%% Possibly use gen_tcp and sockets again to send a message
%% to another computer to get some data in order to
%% calculate Result1:
Result1.
computerInFranceProcess(RawData) ->
...
Result2.
computerInRussiaProcess(RawData) ->
...
Result3.
我不什么不解的是如何以及在何处handle_call,handle_cast玩 到服务器体系结构 - 我是否理解服务器从客户机 - >服务器体系结构(直到我遇到 混淆)的流程。我认为这非常重要,以说明流程图非常类似于电路图。
Client:
+------------------------------------------------------+------------------------------------------------------+
| my_request() -> | handle_call({dostuff, Val}, ClientPid, State) -> |
| Request = {dostuff, 10}, | %%Do something with Val, State |
| Server = ?MODULE, | Response = {result, 45}, |
| Response = gen_server:call(Server, Request). | NewState = ...., |
| | | {Response, NewState}. |
| | from gen_server: | |
| | start_link() | ^ |
| | | | | |
+----------------------------+-----------------+-------+-------------------------------------+----------------+
| | |
| | |
+----------------------------+-----------------+-------+ |
|-module(gen_server). | | | |
|-export([call/2,....]). V | | |
| | | |
|call(Server, Request) -> V | |
| Server ! {request, Request, call, self(), Module} --+-->+ |
| receive | | ^
| {reply, Response, Server} -> | | |
| Response ^ | V |
| end. | | | |
+------------------------+-----------------------------+ | |
| Mailbox | | | |
| | | | |
| {reply, Response, Server} <----------<--------+---+--------------<--------------+ |
| | V ^^
+------------------------------------------------------+ | | |
| | |
| | |
Server: | | |
+------------------------------------------------------+ | | |
| Mailbox | | | |
| | V ^^
| {request, Request, call, ClientPid, Module} <-+---+ | |
| | | | |
+----------------------------+-------------------------+-----------------------------+ | |
| | | | |
|loop(State) -> | | | |
| receive V | ^^
| {request, Request, call, ClientPid, Module} -> | | | ^
| {Response, NewState} = Module:handle_call(Request, ClientPid, State} ---+---|-->+ |
| ClientPid ! {reply, Response, self()}, ----------->---------------------+-->+ To Client
| loop(NewState); | ^
| {request, Request, cast, ClientPid, Module} -> | |
| NewState = Module:handle_cast(Request, State), ------->---------->------|----->------------>+
| loop(NewState); |
| ... |
| ... |
| end. |
+------------------------------------------------------------------------------------+
当客户端调用gen_server:call()
流量:
客户端调用gen_server:start_link()
其中至少要指定在其中handle_call/handle_cast函数定义的模块。
客户端调用gen_server:call(ServerName, Request)
,它通常包含在一个接口函数中。
gen_server:呼叫(服务器,请求)被定义为send a message to the server
,这样的事情:
ServerName ! {request, Request, call, self(), ModName}.
的modname先前绑定到在gen_server指定的原子:START_LINK():第二个参数是在那里你当服务器接收到该消息指定所包含的功能handle_call()的定义,handle_cast()模块名称等
,服务器调用ModName:handle_call()
,和你的modname:handle_call()的代码做一些事情请求:
handle_call(Request, ClientPid, ServerLoopValue) ->
%%Compute some result using information in Request/ServerLoopValue
您的modname的最后一行:handle_call()函数告诉服务器如何发送回客户端为a response
:
{Response, NewServerLoopValue}.
然后服务器确实是这样的:
From ! {reply, Response, ServerPid}.
loop(NewServerLoopValue).
和NewServerLoopValue成为服务器的循环()的新变量global变量。每个服务器有一个循环()函数,它看起来是这样的:
loop(ServerLoopValue) ->
receive
{request, dothis, From} ->
Result1 = ...SomeValue + 5....,
From ! {Result1, self()},
loop(NewServerLoopValue);
{request, {dothat, 10}, From} ->
Result2 = ... SomeValue - 10...,
From ! {Result2, self()},
loop(NewServerLoopValue);
{request, stop, From}
%%do not call loop()
end.
ServerLoopValue就像是一个全球变量,所有的不同的要求可以看出。各种gen_server请求处理程序可以使用存储在ServerLoopValue中的信息来计算响应,也可以将信息添加到其他请求处理程序将来可以使用的ServerLoopValue中。
流量使用TCP套接字一个gen_server的后门与{active, true}, {packet, 4}
打算:
客户端调用gen_tcp:send()
。
在服务器端的套接字中,Erlang从套接字中读取数据,构造一个消息元组,并将消息元组放入server's mailbox
。
服务器从邮箱中检索{tcp,...}消息,并调用handle_info()
。
handle_info()调用gen_tcp:send(Socket, Response)
将响应发送回客户端。
handle_info()的最后一行告诉调用服务器的循环()函数时使用何种价值的服务器:
{noreply, SomeValue} => loop(SomeValue)
流量使用的gen_server的后门去与{active, false}, {packet, 0}
TCP套接字:
Erlang gen_tcp not receiving anything
谢谢!当我在每个函数上进行一些io:format调用来查看流程时,这会更有意义。 – user954753