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());
}