MMORPG大型游戏设计与开发(part5 of net)

时间:2023-01-06 03:57:28
上一部分将服务器的具体代码的实现介绍给了大家,想必大家也了解到了服务器处理一次消息的复杂度。如果大家能够将各个过程掌握清楚,就会发觉其实整个逻辑与交互过程是比较清晰的。那么服务器与服务器之间的通讯,其实也就是相当于客户端与服务器的通讯又是如何实现的呢?本文将用一个实例来将这个过程展示给大家。

 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

   服务器启动:

MMORPG大型游戏设计与开发(part5 of net)

  这个例子中,我以两个服务器作为客户端,分别在windows和linux平台下。

LINUX

  配置:

MMORPG大型游戏设计与开发(part5 of net)

MMORPG大型游戏设计与开发(part5 of net)

  连接失败:

MMORPG大型游戏设计与开发(part5 of net)

连接成功:

MMORPG大型游戏设计与开发(part5 of net)

  服务器接收成功:

MMORPG大型游戏设计与开发(part5 of net)

WINDOWS

  配置:

MMORPG大型游戏设计与开发(part5 of net)

MMORPG大型游戏设计与开发(part5 of net)

  连接失败:

MMORPG大型游戏设计与开发(part5 of net)

  连接成功:

MMORPG大型游戏设计与开发(part5 of net)

  服务器接收成功:

MMORPG大型游戏设计与开发(part5 of net)

 

SERVER

  同时接收连接:

MMORPG大型游戏设计与开发(part5 of net)

  下一部分将对网络部分的设计进行总结,同时这些文章也会不断更新。