首页 > 代码库 > 魔兽世界服务器Trinitycore分析三:auth server网络事件的处理
魔兽世界服务器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基本的网络事件的处理就介绍到这里了,希望大家喜欢。
魔兽世界服务器Trinitycore分析三:auth server网络事件的处理