魔兽世界服务器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基本的网络事件的处理就介绍到这里了,希望大家喜欢。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值