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

CODE

bool ServerManager::connectserver() {
uint8_t step = ;
__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 = ;
billingsocket = billing_serverconnection_.getsocket();
try {
result = billingsocket->create();
if (!result) {
step = ;
Assert(false);
}
result = billingsocket->connect(
kServerIp,
kServerPort);
if (!result) {
step = ;
printf("exception 2");
goto EXCEPTION;
Assert(false);
}
result = billingsocket->set_nonblocking();
if (!result) {
step = ;
printf("exception 3");
Assert(false);
} result = billingsocket->setlinger();
if (!result) {
step = ;
printf("exception 4");
Assert(false);
}
g_log->fast_save_log(kBillingLogFile,
"ServerManager::connectserver()"
" ip:%s, port: %d, success",
kServerIp,
kServerPort);
}
catch(...) {
step = ;
Assert(false);
}
result = addconnection(
(pap_server_common_net::connection::Base*)&billing_serverconnection_);
if (!result) {
step = ;
Assert(false);
}
connectpacket = new pap_server_common_net::packets::serverserver::Connect();
connectpacket->set_serverid();
connectpacket->set_worldid();
connectpacket->set_zoneid();
result = billing_serverconnection_.sendpacket(connectpacket);
SAFE_DELETE(connectpacket);
if (!result) {
step = ;
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 = ; 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 = ; 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

  同时接收连接:

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

MMORPG大型游戏设计与开发(part5 of net)的更多相关文章

  1. MMORPG大型游戏设计与开发(概述)updated

    1.定义 MMORPG,是英文Massive(或Massively)Multiplayer Online Role-PlayingGame的缩写,即大型多人在线角色扮演游戏. 2.技术与知识 在这系列 ...

  2. MMORPG大型游戏设计与开发(UI SYSTEM SHOW)

    接下来一段时间,这些文件可能不再更新,期间我会学习和掌握一些前端知识.虽然我非常欣赏剑侠网络版叁和九阴真经的画面,但是那是一个庞大的游戏引擎,一般人是无法窥伺的,除非你是天才而且要拥有机器毫无中断的毅 ...

  3. MMORPG大型游戏设计与开发(服务器 游戏场景 核心详述)

    核心这个词来的是多么的高深,可能我们也因为这个字眼望而却步,也就很难去掌握这部分的知识.之所以将核心放在最前面讲解,也可以看出它真的很重要,希望朋友们不会错过这个一直以来让大家不熟悉的知识,同我一起进 ...

  4. MMORPG大型游戏设计与开发(游戏服务器 游戏场景 概述 updated)

    我们在玩游戏的时候,我们进入游戏后第一眼往往都是看到游戏世界中的场景,当然除了个别例外,因为那些游戏将游戏场景隐藏了起来,如文字游戏中的地点一样.既然我们接触了游戏世界的核心,那么作为核心的场景又包括 ...

  5. MMORPG大型游戏设计与开发(客户端架构 part8 of vegine)

    脚本模块是游戏设计中争论比较多的话题,那是因为作为脚本本身所带来的利弊.其实这都无关紧要,取舍是人必须学会的一项技能,如果你不会取舍那么就让趋势给你一个满意的答复.自从魔兽世界以及传奇(世界)问世以来 ...

  6. MMORPG大型游戏设计与开发(客户端架构 part5 of vegine)

    客户端异常捕获,是一件必然的事情,特别是在开发的时候就更需要这些有利于找出问题原因的捷径.区别于服务器的是,客户端基本上是以界面为主,你很难在正常运行程序的情况下进行一些输出的监视,如一些日志的记录. ...

  7. MMORPG大型游戏设计与开发(客户端架构 part12 of vegine)

    在游戏中的交互过程中输入是一个必不可少的过程,比如登陆的时候需要用户输入用户名与密码,就算是单机游戏很多时候也要求用户输入一个用户名作为存档的依据.网络游戏中没有了输入,只用鼠标来交互是不切实际的,因 ...

  8. MMORPG大型游戏设计与开发(part1 of net)

    网络模块的设计,是大型多人在线游戏中比较重要的一部分.我之所以将网络模块放到最前面,是因为许许多多的开发者面对这一块的时候充满了疑惑,而且也觉得很神秘和深奥.这些我们面对到的困难,其实是由于我们对这方 ...

  9. MMORPG大型游戏设计与开发(规范)

    一件事如果没有规范.章法,那么做这件事起来往往会遇到许多难题,特别是在多人协作的时候,没有到规范通常让每个人多多少少都面临着头疼的困难.举个例子,多个人要做一桌美味的饺子,有买材料.做面皮.弄肉(菜) ...

随机推荐

  1. Xslider演示8种滚动效果

    Xslider演示8种滚动效果包括: 一.左右切换:每次移动固定距离 二.左右切换:最后一个显示在最右侧 三.自动切换 四.循环切换 五.文本的上下滚动 六.上下切换 七.上下自动循环切换 在线预览 ...

  2. Intercooler.js – 让 AJAX 像锚标签一样简单

    使用 Intercooler,你可以添加 Ajax 到你的应用程序,而无需使用客户端模式的路由,认证,渲染,工厂或依赖注入.事实上,你不需要写任何的 JavaScript 代码.Intercooler ...

  3. Unsplash.it - 实用的图片占位符,支持个性化设置

    Unsplash.it 是一个使用漂亮的图像作为占位符的工具.只要把你的图像尺寸(宽与高)放到网址后面的参数中,你会得到一个占位符.你可以很容易地得到一个随机图像或者是一个模糊图像.也支持获取灰度图像 ...

  4. windows 安装 go语言

    下载地址:http://www.cnblogs.com/osfipin/p/4856839.html. 官方教程说明: Windows 对于Windows用户,Go项目提供两种安装选项(从源码安装除外 ...

  5. asp.net中控制反转的理解

    对IOC的解释为:“Inversion of control is a common characteristic of frameworks, so saying that these lightw ...

  6. 创建和删除节点:——核心DOM

    1. 创建单个元素节点:3步:       1. 创建空元素节点对象:          var elem=document.createElement("标签名");      ...

  7. Day Tips:分布式缓存的删除和重建

    遇到cacheHostInfo is null 错误时,必须将这台服务器上的实例删除重新创建 $instanceName ="SPDistributedCacheService Name=A ...

  8. ContentProvider实现流程

    个人记录 public class DataBaseContentProvider extends ContentProvider { private SQLiteOpenHelper mSQLite ...

  9. node.js Tools for Visual Studio 介绍

    node.js Tools for Visual Studio简称NTVS 项目 安装包地址:https://nodejstools.codeplex.com 目前支持2012和2013

  10. Android Studio简单设置

    IDE外观&行为 修改主题,修改全局字体 修改主题,想用炫酷的深色主题,就改成Darcula吧:字体的话,选一个带中文的,要不然会有很多口口,我这里用Microsoft YaHei UI,很不 ...