魔兽世界服务器Trinitycore分析三:auth server网络事件的处理

authserver在初始化静态变量时,会调用AuthSession::InitHandlers(),初始化全局的业务哈希表Handlers:

std::unordered_map<uint8, AuthHandler> AuthSession::InitHandlers()
{
    std::unordered_map<uint8, AuthHandler> handlers;

    handlers[AUTH_LOGON_CHALLENGE]     = { STATUS_CONNECTED, sizeof(AUTH_LOGON_CHALLENGE_C), &AuthSession::HandleLogonChallenge };
    handlers[AUTH_LOGON_PROOF]         = { STATUS_CONNECTED, sizeof(AUTH_LOGON_PROOF_C),     &AuthSession::HandleLogonProof };
    handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CONNECTED, sizeof(AUTH_LOGON_CHALLENGE_C), &AuthSession::HandleReconnectChallenge };
    handlers[AUTH_RECONNECT_PROOF]     = { STATUS_CONNECTED, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof };
    handlers[REALM_LIST]               = { STATUS_AUTHED,    REALM_LIST_PACKET_SIZE,         &AuthSession::HandleRealmList };
    handlers[XFER_ACCEPT]              = { STATUS_AUTHED,    XFER_ACCEPT_SIZE,               &AuthSession::HandleXferAccept };
    handlers[XFER_RESUME]              = { STATUS_AUTHED,    XFER_RESUME_SIZE,               &AuthSession::HandleXferResume };
    handlers[XFER_CANCEL]              = { STATUS_AUTHED,    XFER_CANCEL_SIZE,               &AuthSession::HandleXferCancel };

    return handlers;
}

std::unordered_map<uint8, AuthHandler> const Handlers = AuthSession::InitHandlers();

进入main函数之后,程序会创建一个AsyncAcceptor<AuthSession>对象,在它的构造函数中,会调用AsyncAcceptor::AsyncAccept()准备监听指定端口,当然,正式开始监听要等开始事件循环之后,即调用_ioService.run()之后:

//这个函数里用了C++11的lambda表达式,智能指针,move语义,如果不熟悉这些,请先看一下C++11的教程
void AsyncAccept()
{
    _acceptor.async_accept(_socket, [this](boost::system::error_code error)
    {
        if (!error)
        {
            try
			{
                // this-> is required here to fix an segmentation fault in gcc 4.7.2 - reason is lambdas in a templated class
                std::make_shared<T>(std::move(this->_socket))->Start();
            }
            catch (boost::system::system_error const& err)
            {
                TC_LOG_INFO("network", "Failed to retrieve client's remote address %s", err.what());
            }
        }

        // lets slap some more this-> on this so we can fix this bug with gcc 4.7.2 throwing internals in yo face
		this->AsyncAccept();
	});
}<span style="font-family: Arial, Helvetica, sans-serif;"> </span>

如上图所示,当客户端发起链接请求时,服务器会用智能指针新建一个AuthSession,然后调用其Start函数,它除了清空缓冲区之外,最主要是调用Socket类(AuthSession的基类)的AsyncReadMissingHeaderData函数,注册一个异步的读请求,读取包头:

void AsyncReadMissingHeaderData()
{
    _socket.async_read_some(boost::asio::buffer(_readHeaderBuffer.GetWritePointer(), std::min<std::size_t>(READ_BLOCK_SIZE, _readHeaderBuffer.GetMissingSize())),
        std::bind(&Socket<T, PacketType>::ReadHeaderHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2));
}

读取到包头之后,会调用ReadHeaderHandlerInternal函数,它主要是调用了ReadHeaderHandler函数(当然还有一堆的错误判断)

void AuthSession::ReadHeaderHandler()
{
    uint8 cmd = GetHeaderBuffer()[0];
    auto itr = Handlers.find(cmd);
    if (itr != Handlers.end())
    {
        // Handle dynamic size packet
        if (cmd == AUTH_LOGON_CHALLENGE || cmd == AUTH_RECONNECT_CHALLENGE)
        {
            ReadData(sizeof(uint8) + sizeof(uint16)); //error + size
            sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetDataBuffer());

            AsyncReadData(challenge->size);
        }
        else
            AsyncReadData(itr->second.packetSize);
    }
    else
        CloseSocket();
}

如上图所示,它先读取一个字节的协议号(包头),然后再根据该协议号,决定包体的长度size。最后再注册一个读请求(AsyncReadMissingData函数),请求长度为size的数据。一般而言,包体的长度就是结构体的长度,只有AUTH_LOGON_CHALLENGE和AUTH_RECONNECT_CHALLENGE例外,它们的长度是由sAuthLogonChallenge_C::size字段决定(它们用了一个技巧http://www.cnblogs.com/winkyao/archive/2012/02/14/2351885.html):

void AsyncReadMissingData()
{
    _socket.async_read_some(boost::asio::buffer(_readDataBuffer.GetWritePointer(), std::min<std::size_t>(READ_BLOCK_SIZE, _readDataBuffer.GetMissingSize())),
        std::bind(&Socket<T, PacketType>::ReadDataHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2));
}

读取到包体之后,会调用ReadDataHandler函数,它会根据协议号来调用对应的处理函数,处理完后,再注册一个异步的读事件,读取下一个表头:

void AuthSession::ReadDataHandler()
{
    if (!(*this.*Handlers.at(GetHeaderBuffer()[0]).handler)())
    {
        CloseSocket();
        return;
    }

    AsyncReadHeader();
}

在处理函数中,首先会将收到的数据强制转换成对应的结构体,如:

sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetDataBuffer());

然后再进行处理,处理过程中,会将要发给客户端的数据打包成一个ByteBuffer,然后再调用AsyncWrite函数,异步发给客户端:

void AuthSession::AsyncWrite(ByteBuffer& packet)
{
    if (!IsOpen())
        return;

    std::lock_guard<std::mutex> guard(_writeLock);

    bool needsWriteStart = _writeQueue.empty();

    _writeQueue.push(std::move(packet));

    if (needsWriteStart)
        Base::AsyncWrite(_writeQueue.front());
}

当客户端与服务器断开时(网络错误,帐号密码错误,或登陆成功都会触发此事件),服务器会收到一个error_code不为0的读事件,调用了CloseSocket函数:

virtual void CloseSocket()
{
    if (_closed.exchange(true))
        return;

    boost::system::error_code shutdownError;
    _socket.shutdown(boost::asio::socket_base::shutdown_send, shutdownError);
    if (shutdownError)
        TC_LOG_DEBUG("network", "Socket::CloseSocket: %s errored when shutting down socket: %i (%s)", GetRemoteIpAddress().to_string().c_str(),
            shutdownError.value(), shutdownError.message().c_str());
}

auth server基本的网络事件的处理就介绍到这里了,希望大家喜欢。

时间: 2024-10-05 12:19:56

魔兽世界服务器Trinitycore分析三:auth server网络事件的处理的相关文章

魔兽世界服务器Trinitycore分析三:日志模块

一:日志接口 TrinityCore的日志有六个级别:Trace,Debug,Info,Warn,Error,Fatal 它们分别对应以下这六个接口,(filterType__也是在配置文件中指定的) #define TC_LOG_TRACE(filterType__, ...) #define TC_LOG_DEBUG(filterType__, ...) #define TC_LOG_INFO(filterType__, ...) #define TC_LOG_WARN(filterType

魔兽世界服务器Trinitycore分析二:auth server的main函数

TrinityCore由生成两个执行文件authserver和world server以及一堆DLL(或so)文件的子项目组成(先忽略map_extractor等几个工具项目). authserver是登录验证服,它主要提供登录验证和获取服务器(区服)列表的功能.world server是真正的游戏服务器,提供游戏里的一切功能.相对而言,auth server比较简单,那我就从它入手啊,先看一下它的main函数,在源码的基础上,我加上了注释. //该函数在src\server\authserve

魔兽世界服务器Trinitycore分析一:前言

一:简介 帖一段官网介绍吧 TrinityCore is a MMORPG Framework based mostly in C++. Itis derived from MaNGOS,the Massive Network Game Object Server,and is based on the code of that project with extensive changes over time tooptimize, improve and cleanup the codebas

内网PC通过NAT server公网地址访问内部服务器时TCP三次握手不成功

公网地址访问内部服务器时TCP三次握手不成功   一.  背景 在上图所示的网络中,PC 及Server属不同LAN,都是zone trust.为了让Internet用户能够访问到Server,FW上部署了NatServer:nat server global A.B.C.D inside 192.168.51.M也就是将公网地址A.B.C.D映射到192.168.51.M.完成上述配置后,Internet用户能够通过A.B.C.D这个公网IP访问Server.但是内网的PC在访问Server的

飞鸽传书源码分析三-网络

转载请注明出处:http://blog.csdn.net/mxway/article/details/44195099 本文是在飞鸽传书2.06源码基础之上进行分析的. 一.网络的初始化 飞鸽传书主窗口对应的类TMainWin的构造函数中有如下的代码 cfg = new Cfg(nicAddr, portNo = _portNo); if ((msgMng = new MsgMng(nicAddr, portNo, cfg))->GetStatus() == FALSE) { ::ExitPro

三大WEB服务器对比分析(apache ,lighttpd,nginx)

一.软件介绍(apache  lighttpd  nginx) 1. lighttpd Lighttpd是一个具有非常低的内存开销,cpu占用率低,效能好,以及丰富的模块等特点.lighttpd是众多OpenSource轻量级的web server中较为优秀的一个.支持FastCGI, CGI, Auth, 输出压缩(output compress), URL重写, Alias等重要功能. Lighttpd使用fastcgi方式运行php,它会使用很少的PHP进程响应很大的并发量. Fastcg

开发与测试分析工具集锦(网络摘要)不断更新

一.测试工具 (一)软件测试工具 (二)网络分析工具 1.TcpDump工具(网络安全分析工具 ):抓包分析                     tcpdump 是一个运行在命令行下的嗅探工具.它允许用户拦截和显示发送或收到过网络连接到该计算机的TCP/IP和其他数据包.tcpdump 是一个在BSD许可证下发布的自由软件.                     tcpdump是非常强大的网络安全分析工具,可以将网络上截获的数据包保存到文件以备分析.可以定义过滤规则,只截获感兴趣的数据包,

2-4-RHEL6.3搭建samba服务器案例分析与总结(Red Hat Enterprise Linux Server6.3)@树袋飘零

本节介绍内容: 1.  samba概述 2.  samba服务器的搭建 3.  samba服务主配置文件的详解 4.  samba服务器搭建案例分析 1.  samba概述 samba是linux以及UNIX和windows完美交互的工具.我们首先来说下samba的由来,那要先从SMB说起.SMB即(Server Message Block )服务器消息块,SMB主要是Microsoft的网络通讯协议,后来应用在了linux上,形成了samba,这是一个能让linux系统应用Microsoft网

2-3-RHEL6.3搭建NTP 服务器案例分析与总结(RedHat Enterprise Linux Server6.3)@树袋飘零

本节介绍内容: 1.  samba概述 2.  samba服务器的搭建 3.  samba服务主配置文件的详解 4.  samba服务器搭建案例分析 1.  samba概述 samba是linux以及UNIX和windows完美交互的工具.我们首先来说下samba的由来,那要先从SMB说起.SMB即(Server Message Block )服务器消息块,SMB主要是Microsoft的网络通讯协议,后来应用在了linux上,形成了samba,这是一个能让linux系统应用Microsoft网