B.服务器代码
抱歉的是,服务器目前的代码要比消息路由的代码还要多,因此只有核心:
服务器在启动过程中,通过下面的代码来检测数据库是否准备好了:
mnesia:start(),
case mnesia:wait_for_tables([counter], 100) of
{timeout,_} ->
io:format("ready create_tables~n",[]),
schema:create_table([game,player,counter]);
ok ->
ok
end,
如果检查通过的话,会再次调用mnesia:wait_for_tables,因为在没有通过的时候,会自动创建数据库,虽然复杂了写,没什么坏处的。
服务器在初始化部分,会启动socket服务器的代码,让socket端口处于准备接收的状态。socket的代码没记错的话,Joe Armstrong在可容错服务器的教程中说了很多,当然,这里最后也会提供一份非常相似的。
下面的代码,应该是属于server部分,但却是在socket的进程中进行执行的:
%% @spec loop(Socket,Client) -> ok
%% @doc sokcet接口的回调函数,当socket接受到一个数据包时,将调用本函数.
loop(Socket, Client) ->
receive
{tcp, Socket, Bin } ->
case proto:read(Bin) of
{?PP_REGISTER, Nick, Pass} ->
case login:creat(Nick, Pass) of
{ok, _} ->
case login:login(Nick, Pass, self()) of
{ error, Error } ->
io:format("login error: ~w: ~w: ~w : ~n", [Error, ?MODULE, ?LINE]),
ok = ?tcpsend(Socket, {?ERROR_MESSAGE, Error}),
loop(Socket, Client);
{ok, Player} ->
router:login(Player#player.id, self()),
Client1 = Client#client{player = Player},
ok = ?tcpsend(Socket, {?PP_PID, Player#player.id}),
loop(Socket, Client1)
end;
{error, Msg} ->
ok = ?tcpsend(Socket, {?ERROR_MESSAGE, Msg}),
loop(Socket, Client)
end;
{?PP_LOGIN, Nick, Pass } ->
case login:login(Nick, Pass, self()) of
{ error, Error } ->
io:format("login error: ~w: ~w : ~w: ~n", [Error, ?MODULE, ?LINE]),
ok = ?tcpsend(Socket, {?ERROR_MESSAGE, Error}),
loop(Socket, Client);
{ok, Player} ->
router:login(Player#player.id, self()),
Client1 = Client#client{player = Player},
ok = ?tcpsend(Socket, {?PP_PID, Player#player.id}),
loop(Socket, Client1)
end;
Other ->
io:format("receive otherClient=~w:~w~n",[Client, Other]),
loop(Socket, Client)
end;
{tcp_closed, Socket} ->
ok;
{packet, Packet } ->
ok = ?tcpsend(Socket, Packet),
loop(Socket, Client);
Other->
io:format("server other:~w~n",[Other]),
loop(Socket, Client)
end.
当从socket接收到数据包的时候,通过proto:read将数据包进行解析,变成erlang可以看的懂的结构。当接收到register的消息的时候,就创建一个新的用户,然后在服务器端模拟用户登录,将登录后的成功的消息发送给玩家,如果发生非常不幸的事情,比如用户名被占用了,则告诉玩家出错的消息。还有处理登录的消息,如果出错,会告诉玩家出错的消息。还有就是其他的消息了。erlang的设计非常的严密,每个函数都必须要有返回值,如果一旦发生不匹配,或者无法处理的消息,则很容易就引起执行该段代码的进程的终止。