MMORPG大型游戏设计与开发(part5 of net)
上一部分将服务器的具体代码的实现介绍给了大家,想必大家也了解到了服务器处理一次消息的复杂度。如果大家能够将各个过程掌握清楚,就会发觉其实整个逻辑与交互过程是比较清晰的。那么服务器与服务器之间的通讯,其实也就是相当于客户端与服务器的通讯又是如何实现的呢?本文将用一个实例来将这个过程展示给大家。
CODE
bool ServerManager::connectserver() { uint8_t step = 0; __ENTER_FUNCTION bool result = false; pap_server_common_net::packets::serverserver::Connect* connectpacket = NULL; pap_common_net::socket::Base* billingsocket = NULL; const char *kServerIp = "192.168.200.100"; const uint16_t kServerPort = 12680; billingsocket = billing_serverconnection_.getsocket(); try { result = billingsocket->create(); if (!result) { step = 1; Assert(false); } result = billingsocket->connect( kServerIp, kServerPort); if (!result) { step = 2; printf("exception 2"); goto EXCEPTION; Assert(false); } result = billingsocket->set_nonblocking(); if (!result) { step = 3; printf("exception 3"); Assert(false); } result = billingsocket->setlinger(0); if (!result) { step = 4; printf("exception 4"); Assert(false); } g_log->fast_save_log(kBillingLogFile, "ServerManager::connectserver()" " ip:%s, port: %d, success", kServerIp, kServerPort); } catch(...) { step = 5; Assert(false); } result = addconnection( (pap_server_common_net::connection::Base*)&billing_serverconnection_); if (!result) { step = 6; Assert(false); } connectpacket = new pap_server_common_net::packets::serverserver::Connect(); connectpacket->set_serverid(9999); connectpacket->set_worldid(0); connectpacket->set_zoneid(0); result = billing_serverconnection_.sendpacket(connectpacket); SAFE_DELETE(connectpacket); if (!result) { step = 7; Assert(false); } g_log->fast_save_log(kBillingLogFile, "ServerManager::connectserver() is success!"); return true; EXCEPTION: g_log->fast_save_log( kBillingLogFile, "ServerManager::connectserver() have error, ip: %s, port: %d, step: %d", kServerIp, kServerPort, step); billing_serverconnection_.cleanup(); return false; __LEAVE_FUNCTION return false; }
我在这里简单介绍下以上代码:billing_serverconnection_是服务器的主连接,我们以这个连接连接到目标IP为192.168.200.100,port为12680的服务器。连接成功后,我们会发送一个连接信息的包到目标服务器上。可以看出来这个包里设置了三个参数,serverid(服务器id)、worldid(世界id)、zoneid(区域id)。至于这个方法需要放置的位置,必须置于服务器初始化完成后,因为我们要让他创建一个可用的主套接字连接。
接着我们看看在服务器,执行完这个方法后又进行了哪些处理。
bool ServerManager::processoutput() { __ENTER_FUNCTION if (SOCKET_INVALID == maxfd_&& SOCKET_INVALID == minfd_) return false; uint16_t i; uint16_t connectioncount = billingconnection::Manager::getcount(); for (i = 0; i < connectioncount; ++i) { if (ID_INVALID == connectionids_[i]) continue; billingconnection::Server* serverconnection = NULL; //serverconnection = g_connectionpool->get(connectionids_[i]); serverconnection = &billing_serverconnection_; Assert(serverconnection); int32_t socketid = serverconnection->getsocket()->getid(); if (socketid_ == socketid) continue; if (FD_ISSET(socketid, &writefds_[kSelectUse])) { if (serverconnection->getsocket()->iserror()) { removeconnection(serverconnection); } else { try { if (!serverconnection->processoutput()) removeconnection(serverconnection); } catch(...) { removeconnection(serverconnection); } } } } return true; __LEAVE_FUNCTION return false; }
bool ServerManager::processcommand() { __ENTER_FUNCTION if (SOCKET_INVALID == maxfd_&& SOCKET_INVALID == minfd_) return false; uint16_t i; uint16_t connectioncount = billingconnection::Manager::getcount(); for (i = 0; i < connectioncount; ++i) { if (ID_INVALID == connectionids_[i]) continue; billingconnection::Server* serverconnection = NULL; //serverconnection = g_connectionpool->get(connectionids_[i]); serverconnection = &billing_serverconnection_; Assert(serverconnection); int32_t socketid = serverconnection->getsocket()->getid(); if (socketid_ == socketid) continue; if (serverconnection->getsocket()->iserror()) { removeconnection(serverconnection); } else { //connection is ok try { if (!serverconnection->processcommand(false)) removeconnection(serverconnection); } catch(...) { removeconnection(serverconnection); } } } return true; __LEAVE_FUNCTION return false; }
特别注意代码中的注释部分,因为验证服务器是等待连接的,没有这种主动连接别的服务器的需要,所以整个功能设计上没有这部分,这个连接的方法也是我临时添加上的。既然是验证服务器的连接发送出去的包,那么对应处理输出流和命令处理自然也应该是这个连接,否则我们的包就发送不出去了。
下面我们就来看看服务器端对这个包的处理代码(handler):
#include "server/common/net/packets/serverserver/connect.h" #include "server/billing/connection/server.h" #include "server/common/base/log.h" namespace pap_server_common_net { namespace packets { namespace serverserver { uint32_t ConnectHandler::execute(Connect* packet, connection::Base* connection) { __ENTER_FUNCTION g_log->fast_save_log(kBillingLogFile, "ConnectHandler::execute(...) serverid: %d ...success", packet->get_serverid()); return kPacketExecuteStatusContinue; __LEAVE_FUNCTION return kPacketExecuteStatusError; } } //namespace serverserver } //namespace packets } //namespace pap_server_common_net
包收到后,我们需要进行一段逻辑处理,这个包是最简单的连接,只是对包里面这个服务器id进行了输出处理。
RESULT
服务器启动:
这个例子中,我以两个服务器作为客户端,分别在windows和linux平台下。
LINUX
配置:
连接失败:
连接成功:
服务器接收成功:
WINDOWS
配置:
连接失败:
连接成功:
服务器接收成功:
SERVER
同时接收连接:
下一部分将对网络部分的设计进行总结,同时这些文章也会不断更新。
作者:viticm
出处: http://www.cnblogs.com/lianyue/