一起做网游吧【7】:服务器端注册和登录处理

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的设计非常的严密,每个函数都必须要有返回值,如果一旦发生不匹配,或者无法处理的消息,则很容易就引起执行该段代码的进程的终止。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据