本文是通过TrinityCore研究从而对MMORPG服务器设计分析。
网络字节序
ByteBuffer是字节(unsigned char类型)处理,没有大小端处理。在网络通信应用程序上,这种是经常有的,网络是通过字节发送。
消息包格式:包头+包类型+包体
包头rc4加密
登录认证是(srp6)安全远程密码第六版协议,包类型是占一字节。
游戏中,包类型占1.5字节。
1、封包
重载&operator<<
按顺序把数据封包
2、解包
重载&operator>>
按顺序把数据解出
消息包处理
网络是采用boost asio
1、发送
这里需要注意,在这个执行,在多线程里面操作加锁
std::unique_lock<std::mutex> guard(_writeLock);
boost::asio的发送函数:_socket.async_write_some
1.1 登录认证的发送
void AuthSession::SendPacket(ByteBuffer& packet) { if (!IsOpen()) return; if (!packet.empty()) { MessageBuffer buffer; buffer.Write(packet.contents(), packet.size()); std::unique_lock<std::mutex> guard(_writeLock); QueuePacket(std::move(buffer), guard); } }
2.2在游戏中发送
void WorldSocket::SendPacket(WorldPacket const& packet) { if (!IsOpen()) return; if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(packet, SERVER_TO_CLIENT, GetRemoteIpAddress(), GetRemotePort()); ServerPktHeader header(packet.size() + 2, packet.GetOpcode()); std::unique_lock<std::mutex> guard(_writeLock); _authCrypt.EncryptSend(header.header, header.getHeaderLength()); #ifndef TC_SOCKET_USE_IOCP if (_writeQueue.empty() && _writeBuffer.GetRemainingSpace() >= header.getHeaderLength() + packet.size()) { _writeBuffer.Write(header.header, header.getHeaderLength()); if (!packet.empty()) _writeBuffer.Write(packet.contents(), packet.size()); } else #endif { MessageBuffer buffer(header.getHeaderLength() + packet.size()); buffer.Write(header.header, header.getHeaderLength()); if (!packet.empty()) buffer.Write(packet.contents(), packet.size()); QueuePacket(std::move(buffer), guard); } }
2、接收
接收函数_socket.async_read_some
处理已收到数据包MessageBuffer& packet = GetReadBuffer();
登录认证接收处理:void WorldSocket::ReadHandler()
游戏过程处理:
void WorldSocket::ReadHandler()
bool WorldSocket::ReadHeaderHandler()
bool WorldSocket::ReadDataHandler()
流程
消息通过函数指针映射处理。
1、登录游戏
std::unordered_map<uint8, AuthHandler> const Handlers = AuthSession::InitHandlers();
2、认证通过后
void WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
内部把这次玩家会话添加 :
新建一个会话_worldSession = new WorldSession(id, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter);
添加到世界:sWorld->AddSession(_worldSession);
判断是否有同个帐号登录,如果有,把它踢下线
2、全部会话处理:
void World::UpdateSessions(uint32 diff) { .. for (SessionMap::iterator itr = m_sessions.begin(), next; itr != m_sessions.end(); itr = next) .. }
2、游戏过程中
extern OpcodeHandler opcodeTable[NUM_MSG_TYPES]
写在后面
TrinityCore常用了TCP通信,tcp通信也是存在心跳的,网络是异常退出(比如,任务管理器直接杀死游戏进程),tcp协议不会通知说我已经退出。如果是实时再线格斗类型游戏,建议常用UDP。TrinityCore大量使用c++ 0x11,有些地方,我也不是明白,如果有存在错误的地方也请指出。