MMORPG大型游戏设计与开发(part4 of net)
上一节简单的介绍了服务器消息处理的流程,想必大家对这方面有了初步的认识,接下来我们需要知道和掌握的便是其中一些重要的方法,进一步深入熟悉整个构架。
1、FD_*系列宏函数
FD_ZERO(fd_set *fdset) 将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
FD_SET(fd_set *fdset) 用于在文件描述符集合中增加一个新的文件描述符。FD_CLR(fd_set *fdset) 用于在文件描述符集合中删除一个文件描述符。
FD_ISSET(int fd,fd_set *fdset) 用于测试指定的文件描述符是否在该集合中。
2、Socket操作类
- /**
- * PAP Engine ( -- )
- * $Id socket.h
- * @link -- for the canonical source repository
- * @copyright Copyright (c) 2013-2013 viticm( viticm@126.com )
- * @license
- * @user viticm<viticm@126.com>
- * @date 2013-12-31 17:34:43
- * @uses server net model socket class
- */
- #ifndef PAP_SERVER_COMMON_NET_SOCKET_H_
- #define PAP_SERVER_COMMON_NET_SOCKET_H_
- #include "common/net/socket/base.h"
- namespace pap_server_common_net {
- class Socket {
- public:
- Socket(uint16_t port, uint32_t backlog = );
- ~Socket();
- public:
- void close();
- bool accept(pap_common_net::socket::Base* socket);
- uint32_t getlinger() const;
- bool setlinger(uint32_t lingertime);
- bool is_nonblocking() const;
- bool set_nonblocking(bool on = true);
- uint32_t getreceive_buffersize() const;
- bool setreceive_buffersize(uint32_t size);
- uint32_t getsend_buffersize() const;
- bool setsend_buffersize(uint32_t size);
- int32_t getid() const;
- protected:
- pap_common_net::socket::Base* socket_;
- };
- }; //namespace pap_server_common_net
- #endif //PAP_SERVER_COMMON_NET_SOCKET_H_
serversocket
这是服务器的socket操作类,构造函数(Socket(uint16_t port, uint32_t backlog = 5))需要提供一个监听端口。
void close(); //关闭套接字。
bool accept(pap_common_net::socket::Base* socket); //接受sokcet连接
uint32_t getlinger() const; //获取延时
bool setlinger(uint32_t lingertime); //设置延时
bool is_nonblocking() const; //是否为non-blocking模式
bool set_nonblocking(bool on = true); //设置non-blocking
uint32_t getreceive_buffersize() const; //获取接收的buffer大小
bool setreceive_buffersize(uint32_t size); //设置接收的buffer大小
uint32_t getsend_buffersize() const; //获取发送的buffer大小
bool setsend_buffersize(uint32_t size); //设置发送buffer大小
int32_t getid() const; //获取套接字ID
其中大家可能不了解的是non-blocking的IO模式,有一篇详细的文中供大家参考:IO模式。
3、套接字输入流和输入流操作类
- #ifndef PAP_COMMON_NET_SOCKET_INPUTSTREAM_H_
- #define PAP_COMMON_NET_SOCKET_INPUTSTREAM_H_
- #include "common/net/config.h"
- #include "common/lib/vnet/vnet.hpp"
- #include "common/net/socket/base.h"
- #include "common/net/packet/base.h"
- namespace pap_common_net {
- namespace socket {
- class InputStream {
- public: //construct and destruct
- InputStream(
- Base* socket,
- uint32_t bufferlength = SOCKETINPUT_BUFFERSIZE_DEFAULT,
- uint32_t bufferlength_max = SOCKETINPUT_DISCONNECT_MAXSIZE);
- virtual ~InputStream();
- public:
- uint32_t read(char* buffer, uint32_t length);
- bool readpacket(packet::Base* packet);
- bool peek(char* buffer, uint32_t length);
- bool skip(uint32_t length);
- uint32_t fill();
- void init();
- bool resize(int32_t size);
- uint32_t reallength();
- bool isempty();
- void cleanup();
- void setkey(unsigned char const* key);
- int32_t get_keylength();
- Base* getsocket();
- private:
- Base* socket_;
- struct packet_t* packet_;
- struct endecode_param_t* endecode_param_;
- };
- }; //namespace socket
- }; //namespace pap_common_net
- #endif //PAP_COMMON_NET_SOCKET_INPUTSTREAM_H_
sokcet_inputstream
套接字的输入流操作类,可以看到构造函数需要提供一个套接字对象初始化。
functions:
uint32_t read(char* buffer, uint32_t length); //读取一段数据
bool readpacket(packet::Base* packet); //读取网络包
bool peek(char* buffer, uint32_t length); //校验数据内容
bool skip(uint32_t length); //跳过大小长度的一段数据
uint32_t fill(); //数据修正
void init(); //初始化
bool resize(int32_t size); //重新分配流大小
uint32_t reallength(); //数据实际长度
bool isempty(); //数据是否为空
void cleanup(); //流内部数据清理
void setkey(unsigned char const* key); //流加密KEY设置,如果设置了这个,则网络数据全部被加密
int32_t get_keylength(); //获取加密KEY的长度
variables:
Base* socket_; //套接字对象
struct packet_t* packet_; //网络包结构
struct endecode_param_t* endecode_param_; //加密数据结构
- #ifndef PAP_COMMON_NET_SOCKET_OUTPUTSTREAM_H_
- #define PAP_COMMON_NET_SOCKET_OUTPUTSTREAM_H_
- #include "common/net/config.h"
- #include "common/lib/vnet/vnet.hpp"
- #include "common/net/socket/base.h"
- #include "common/net/packet/base.h"
- namespace pap_common_net {
- namespace socket {
- class OutputStream {
- public:
- OutputStream(
- socket::Base* socket,
- uint32_t bufferlength = SOCKETOUTPUT_BUFFERSIZE_DEFAULT,
- uint32_t bufferlength_max = SOCKETOUTPUT_DISCONNECT_MAXSIZE);
- ~OutputStream();
- public:
- uint32_t write(const char* buffer, uint32_t length);
- bool writepacket(const packet::Base* packet);
- uint32_t flush();
- void init();
- bool resize(int32_t size);
- uint32_t reallength();
- bool isempty();
- void cleanup();
- void setkey(unsigned char const* key);
- int32_t get_keylength();
- void getbuffer(char* buffer, uint32_t length);
- Base* getsocket();
- private:
- Base* socket_;
- struct packet_t* packet_;
- struct endecode_param_t* endecode_param_;
- };
- }; //namespace socket
- }; //namespace pap_common_net
- #endif //PAP_COMMON_NET_SOCKET_OUTPUTSTREAM_H_
socket_outputstream
其方法和参数其实与输入流大同小异,我在这里就不一一列举了。
4、网络包操作类和网络包管理器
- /**
- * PAP Engine ( -- )
- * $Id packet.h
- * @link -- for the canonical source repository
- * @copyright Copyright (c) 2013-2013 viticm( viticm@126.com )
- * @license
- * @user viticm<viticm@126.com>
- * @date 2014-1-2 11:36:54
- * @uses server and client net pakcet class
- */
- #ifndef PAP_COMMON_NET_PACKET_BASE_H_
- #define PAP_COMMON_NET_PACKET_BASE_H_
- #include "common/net/config.h"
- #include "common/net/socket/inputstream.h"
- #include "common/net/socket/outputstream.h"
- #define GET_PACKETINDEX(a) ((a) >> 24)
- #define SET_PACKETINDEX(a,index) ((a) = (((a) & 0xffffff) + ((index) << 24)))
- #define GET_PACKETLENGTH(a) ((a) & 0xffffff)
- #define SET_PACKETLENGTH(a,length) ((a) = ((a) & 0xff000000) + (length))
- //note cn:
- //消息头中包括:uint16_t - 2字节;uint32_t - 4字节中高位一个字节为消息序列号,
- //其余三个字节为消息长度
- //通过GET_PACKETINDEX和GET_PACKETLENGTH宏,
- //可以取得UINT数据里面的消息序列号和长度
- //通过SET_PACKETINDEX和SET_PACKETLENGTH宏,
- //可以设置UINT数据里面的消息序列号和长度
- #define PACKET_HEADERSIZE (sizeof(uint16_t) + sizeof(uint32_t))
- typedef enum {
- kPacketExecuteStatusError = , //表示出现严重错误,当前连接需要被强制断开
- kPacketExecuteStatusBreak, //表示返回后剩下的消息将不在当前处理循环里处理
- kPacketExecuteStatusContinue, //表示继续在当前循环里执行剩下的消息
- kPacketExecuteStatusNotRemove, //表示继续在当前循环里执行剩下的消息,
- //但是不回收当前消息
- kPacketExecuteStatusNotRemoveError,
- } packet_executestatus_enum;
- namespace pap_common_net {
- namespace packet {
- class Base {
- public:
- Base();
- virtual ~Base();
- public:
- int8_t status_;
- int8_t index_;
- public:
- virtual void cleanup() {};
- virtual bool read(socket::InputStream& inputstream) = ;
- virtual bool write(socket::OutputStream& outputstream) const = ;
- virtual uint32_t execute(
- pap_server_common_net::connection::Base* connection) = ;
- virtual uint16_t getid() const = ;
- virtual uint32_t getsize() const = ;
- int8_t getindex() const;
- void setindex(int8_t index);
- uint8_t getstatus() const;
- void setstatus(uint8_t status);
- };
- }; //namespace packet
- }; //namespace pap_common_net
- #endif //PAP_COMMON_NET_PACKET_BASE_H_
packet_base
functions:
virtual void cleanup() {}; //内部数据清理
virtual bool read(socket::InputStream& inputstream) = 0; //读取网络包
virtual bool write(socket::OutputStream& outputstream) const = 0; //写入网络包
virtual uint32_t execute(
pap_server_common_net::connection::Base* connection) = 0; //网络包执行
virtual uint16_t getid() const = 0; //获取包ID
virtual uint32_t getsize() const = 0; //获取包大小
int8_t getindex() const; //获取包索引
void setindex(int8_t index); //设置包索引
uint8_t getstatus() const; //获取包状态
void setstatus(uint8_t status); //设置包状态
variables:
int8_t status_; //状态
int8_t index_; //索引
- /**
- * PAP Engine ( -- )
- * $Id factorymanager.h
- * @link -- for the canonical source repository
- * @copyright Copyright (c) 2013-2013 viticm( viticm@126.com )
- * @license
- * @user viticm<viticm@126.com>
- * @date 2014-1-3 10:11:38
- * @uses server and client net packet factory manager
- */
- #ifndef PAP_COMMON_NET_PACKET_FACTORYMANAGER_H_
- #define PAP_COMMON_NET_PACKET_FACTORYMANAGER_H_
- #include "common/net/config.h"
- #include "common/net/packet/factory.h"
- #include "common/sys/thread.h"
- namespace pap_common_net {
- namespace packet {
- class FactoryManager {
- public:
- FactoryManager();
- ~FactoryManager();
- public:
- uint32_t* packet_alloccount_;
- public:
- bool init();
- //根据消息类型从内存里分配消息实体数据(允许多线程同时调用)
- Base* createpacket(uint16_t pakcetid);
- //根据消息类型取得对应消息的最大尺寸(允许多线程同时调用)
- uint32_t getpacket_maxsize(uint16_t packetid);
- //删除消息实体(允许多线程同时调用)
- void removepacket(Base* packet);
- void lock();
- void unlock();
- static bool isvalid_packetid(uint16_t id); //packetid is valid
- private:
- Factory** factories_;
- uint16_t size_;
- pap_common_sys::ThreadLock lock_;
- private:
- void addfactory(Factory* factory);
- void addfactories_for_billinglogin();
- void addfactories_for_serverserver();
- void addfactories_for_clientlogin();
- void addfactories_for_loginworld();
- void addfactories_for_serverworld();
- void addfactories_for_clientserver();
- };
- }; //namespace packet
- }; //namespace pap_common_net
- extern pap_common_net::packet::FactoryManager* g_packetfactory_manager;
- #endif //COMMON_NET_PACKETFACTORY_H_
packet_factorymanager
网络包工厂管理器,即是把所有包对象加入到一个管理器中,然后有需要的时候再从工厂中取出来使用。
5、服务器select模式
上一部分的代码中,大家应该也看到了这个代码。那么为什么要select呢?服务器一次普通阻塞之下,一次只能一对一的问答,如果多个同时访问,就要讲究先来后到了。为了避免这样的问题,解决多用户同时访问不需要等待,则使用了select模式。那么我们来看看这个函数的具体用法与解释,还是引用一个前人已经总结出来的文章:socket的select。
下一部分将用一个服务器的实例来讲诉网络部分一次访问的过程。
MMORPG大型游戏设计与开发(part4 of net)的更多相关文章
- MMORPG大型游戏设计与开发(概述)updated
1.定义 MMORPG,是英文Massive(或Massively)Multiplayer Online Role-PlayingGame的缩写,即大型多人在线角色扮演游戏. 2.技术与知识 在这系列 ...
- MMORPG大型游戏设计与开发(UI SYSTEM SHOW)
接下来一段时间,这些文件可能不再更新,期间我会学习和掌握一些前端知识.虽然我非常欣赏剑侠网络版叁和九阴真经的画面,但是那是一个庞大的游戏引擎,一般人是无法窥伺的,除非你是天才而且要拥有机器毫无中断的毅 ...
- MMORPG大型游戏设计与开发(服务器 游戏场景 核心详述)
核心这个词来的是多么的高深,可能我们也因为这个字眼望而却步,也就很难去掌握这部分的知识.之所以将核心放在最前面讲解,也可以看出它真的很重要,希望朋友们不会错过这个一直以来让大家不熟悉的知识,同我一起进 ...
- MMORPG大型游戏设计与开发(游戏服务器 游戏场景 概述 updated)
我们在玩游戏的时候,我们进入游戏后第一眼往往都是看到游戏世界中的场景,当然除了个别例外,因为那些游戏将游戏场景隐藏了起来,如文字游戏中的地点一样.既然我们接触了游戏世界的核心,那么作为核心的场景又包括 ...
- MMORPG大型游戏设计与开发(客户端架构 part8 of vegine)
脚本模块是游戏设计中争论比较多的话题,那是因为作为脚本本身所带来的利弊.其实这都无关紧要,取舍是人必须学会的一项技能,如果你不会取舍那么就让趋势给你一个满意的答复.自从魔兽世界以及传奇(世界)问世以来 ...
- MMORPG大型游戏设计与开发(客户端架构 part4 of vegine)
昨天是七夕,祝大家都过的快乐,希望这句迟到的问候不会造成大家心中的困扰.这一节讲到了前端比较重要的模块,性能以及调试异常模块.一个应用的性能往往是最核心的部分,就像人身体的各个器官一样,一小部分也不能 ...
- MMORPG大型游戏设计与开发(客户端架构 part12 of vegine)
在游戏中的交互过程中输入是一个必不可少的过程,比如登陆的时候需要用户输入用户名与密码,就算是单机游戏很多时候也要求用户输入一个用户名作为存档的依据.网络游戏中没有了输入,只用鼠标来交互是不切实际的,因 ...
- MMORPG大型游戏设计与开发(part1 of net)
网络模块的设计,是大型多人在线游戏中比较重要的一部分.我之所以将网络模块放到最前面,是因为许许多多的开发者面对这一块的时候充满了疑惑,而且也觉得很神秘和深奥.这些我们面对到的困难,其实是由于我们对这方 ...
- MMORPG大型游戏设计与开发(规范)
一件事如果没有规范.章法,那么做这件事起来往往会遇到许多难题,特别是在多人协作的时候,没有到规范通常让每个人多多少少都面临着头疼的困难.举个例子,多个人要做一桌美味的饺子,有买材料.做面皮.弄肉(菜) ...
随机推荐
- Context.js 右键菜单
ContextJS is a lightweight solution for contextual menus. Currently, there are two versions. The fir ...
- 【HTML5】Canvas图像
把一幅图像放置到画布上, 使用以下方法: drawImage(Img,x,y); 注:这里的Img必须是一个图像对象. 显示一个canvas图像: <!DOCTYPE html> &l ...
- flume 集群安装
./pssh -h ./host/all.txt -P mkdir /usr/local/app ./pssh -h ./host/all.txt -P tar zxf /usr/local/soft ...
- Android 监听锁屏、解锁、开屏 操作
1.首先定义 ScreenListener package com.app.lib; import android.content.BroadcastReceiver; import android ...
- Swift - 访问通讯录-使用AddressBook.framework和AddressBookUI.framework框架实现
1,通讯录访问介绍 通讯录(或叫地址簿,电话簿)是一个数据库,里面储存了联系人的相关信息.要实现访问通讯录有如下两种方式: (1)AddressBook.framework框架 : 没有界面,通过代码 ...
- 【读书笔记】iOS网络-负载
负载指的是在服务的请求响应事务中交换的数据.常见的负载格式包括XML,JSON与HTML. 进入与发出的负载数据存在很多形式与大小.比如,有些开发者会使用原生的字符串或是以分隔符分开的数据与Web S ...
- oc TableView 分割线(separator)部分显示问题
问题:当TableView的cell不能显示完整个屏幕(屏幕有剩余),则没有显示cell的地方也会显示分割线,这不是我们想要的,正常情况下,如果没有cell则应没有分割线.如下图所示:左图为遇到问题, ...
- mysql中数据类型的取值范围
mysql整型bigint.int.mediumint.smallint 和 tinyint的语法介绍,如下: 1.bigint 从 -2^63 (-9223372036854775808) 到 2^ ...
- 1.4 基础知识——GP2.2 计划 与 GP2.8 计划跟踪
摘要: CMMI有计划(PP)及计划跟踪(PMC)两个PA,而某一个PA又有GP2.2计划及GP2.8计划跟踪两个GP,看上去是挺“神奇”也挺让人“困惑”的事情. 正文: GP2.2 Establis ...
- GitHub Desktop 桌面工具,离线版本下载(无需考虑网络问题)
http://pan.baidu.com/s/1qYq4X0C GitHub Desktop 桌面工具,离线版本下载 对于网络不好,不稳定,安装多次都不成功的,这是你们的最好的安装方法了.