33 Star 89 Fork 46

leo / Engine

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
README.md 9.44 KB
一键复制 编辑 原始数据 按行查看 历史
leo 提交于 2019-10-21 19:39 . 回滚hiredis

Engine

C++服务器编程底层库

特点

  1. Windows,Linux双平台(Windows下为静态库,主要方便开发者调试;Linux下为动态库,用于生产环境部署)
  2. 基本包含集成服务器常用模块(数学、文件系统、配置、日志、网络、脚本、时间、多线程等)
  3. 二次开发无平台配置,无其他依赖
  4. 基于C++11开发

使用

项目使用xmake管理,使用方法详见xmake手册

注:Linux下建议使用GCC的-Wl,-rpath,.连接选项指定运行期动态连接库的优先查找目录,以方便分发部署

集成第三方说明

  1. Zip使用miniz v2.1.0.
  2. Lua v5.3.5.
  3. 集成Jsoncpp v1.8.4.
  4. hiredis v0.13.3.

模块

程序模型

#include	<app.h>

class GameApp : public Application {
public:
	GameApp() {}

	virtual bool OnInit(const Commandline & cmd) override {
		if (cmd.Has("--debug")) {
			Logger::Instance().Initialize("game", "logs", Logger::Debug);
		} else {
			Logger::Instance().Initialize("game", "logs", Logger::Info);
		}

		/// 锁帧
		LockFPS(20);

		/// 填写其他初始化逻辑,当返回false时程序直接退出
		return true;
	}

	virtual void OnBreath() override {
		/// 这里填写需要每帧更新的逻辑
	}
};

RUN_APP(GameApp)

网络模型

头文件socket.h,一个简单的客户端实现:

class Client : public IOListener {
public:
	Client() : _socket(new Socket()) {
	}

	~Client() {
		Close();
		delete _socket;
	}

	bool Connect(const std::string & ip, int port) {
		bool succ = _socket->Connect(ip, port);
		if (succ) BindIO(_socket->ctx, IO_READ); //! 我们只需要网络有数据来时通知
		return succ;
	}

	/// 实现数据可读时消息处理
	virtual void OnReadable() override {
		char buf[1024] = {0};

		int readed = 0;
		int recv = 0;

		while (true) {
			recv = _socket->Recv(buf, 1024);
			if (recv > 0) {
				//! TODO: process received data.
			} else if (recv == 0) {
				break;
			} else {
				Close(recv);
				break;
			}
		}
	}

	void Close(int reason = 0) {
		UnbindIO(); //! 关闭之前请取消IO事件监听
		_socket->Close();
	}

	bool Send(const char * p, size_t s) {
		return _socket->Send(p, s);
	}

private:
	Socket * _socket;
}

脚本

  1. 设计原则:Lua只负责逻辑,对象生存管理交由C++(可以注册管理到Lua)
  2. 涉及到Get操作,需要try...catch以捕获类型异常(C++注册到Lua的接口内Get不需要,调用函数不需要)
  3. Property可以为地址方式,也可以为Getter(TG (void))、Setter(void (TS))方式注册
  4. Method必须为int (*f)(LuaState &)
  5. Lua不可用于多线程,只能在主线程中使用,但可以使用协程。
#include	<script.h>

/// 注册公共变量或函数到Lua
GLua.Register("GameSetting")	//! 所有下面注册的属性或函数放在GameSetting中
	.Property("nPlayerCounter", &GPlayerCount, false)	//! 以地址方式注册属性,同时设置不可写
	.Property("nTime", &GetTime)	//! 以Getter方式注册属性,同时不注册属性的写方法(不可写)
	.Method("GetAById", &GetAById);	//! 注册全局Lua方法

/// 注册C++类到Lua
GLua.Register<A>("LuaA")
	.Property("nId", &A::id, false)
	.Property("sName", &A::GetName, &A::SetName)
	.Method("Msg", &A::SendMessage);

try {
	A * p = GLua.Get<A *>("me");	// 需要使用try,因为可能类型不匹配
} catch (...) {}

if (GLua.Is<A *>("me")) {} // 不需要try
GLua.Set<A *>("me", new A) // 不需要try
GLua.Call("GameSetting", "GetAById", false, 100); // 不需要try

int GetAById(LuaState & r) {
	int n = r.Get<int>(1);	// 不需要try
	...
}

LUA中扩展C++注册的类或名空间(注只能扩展方法,不可扩展属性)


-- 扩展名空间的方法
function XXX.yyy()
end

-- 扩展类静态方法
function LuaA.Test()
	print("hehe")
end

-- 扩展类成员方法,注意:这里用的是':',因为需要使用self
function LuaA.apis:YYY()
end

内置的其他基本函数

函数 功能
print(...) 使用Logger重载的print接口[Logger::Level::Info]
print_err(...) 使用Logger重载的print接口[Logger::Level::Error]
loadbits(n, start, end) -> integer 读取一个int32中[start, end]字节表示的值
setbits(n, start, end, v) -> integer 设置一个int32中[start, end]字节表示的值
json.encode(v) -> string 将lua变量序列化成json字串
json.decode(s) -> var 将json字串反序列化成lua值
scheduler.timer(delay, func[, is_loop]) -> integer(id) 注册一个定时器
scheduler.task(hour, min, sec, func) -> integer(id) 注册一个每天hour:min:sec执行的操作
scheduler.is_valid(id) -> bool 测试一个定时器或计划是否存在
scheduler.remain(id) -> double 返回一个定时器或计划还需要多少毫秒运行
scheduler.cancel(id) 取消一个定时器或计划任务

内存池

  1. 由于本人能力有限,经实际效率测试,目前仅保留非线程安全的对象Pool(Pool.h)
  2. Pool加锁后可用于多线程,但经测试效率还不及系统的new,但Linux下相差不大,如果考虑到无内存碎片的优点,可以自行添加。
  3. 如果采用Application的模型,Pool基本上是够用的。因为逻辑主要在主线程的Tick中触发

线程池模型

First. 编写线程内的具体工作类,继承IThreadJob.

#include	<Threads.h>

class DemoTask : public IThreadJob {
public:
	DemoTask(...) { ... }			//! 这里为该工作参数初始化
	virtual ~DemoTask() { ... }		//! 这里为工作结束时清理操作

	virtual void OnRun() { ... }	//! 工作的具体内容

private:
	...		//! 参数声明
};

Second. 创建线程池及工作对象容器

int main() {
	Threads workers(4);	//! 创建含有一个4个工作线程的容器

	/// 增加100个并发任务(多余的会暂时等待空闲线程)
	for (int i = 0; i < 100; ++i) {
		workers.AddJob<DemoTask>(...);	// 传入工作需要的参数,这里自动调用 new DemoTask(...);
	}

	/// 等待所有的工作结束,如果不执行该操作,mgr超出生存期时会放弃未执行的任务。
	workers.Wait();
	return 0;
}

Redis客户端

比较简单,请自行阅读redis.h,redis.cc

定时任务

  1. C++接口
/// 添加一个500毫秒后执行的定时器
GScheduler.Add(500, [](uint64_t id) {
	printf("Timer's id : %llu", id);
});

/// 添加一个每500毫秒执行一次的定时器
GScheduler.Add(500, [](uint64_t id) {
	printf("Timer's id : %llu", id);
}, true);

/// 注册每天05:00:00时执行的计划任务
GScheduler.Add(5, 0, 0, [](uint64_t id) {
	printf("Task's id : %llu", id);
});

/// 是否存在定时器或计划任务
bool valid = GScheduler.IsValid(timer_id);

/// 取得一个定时器或计划多少毫秒后执行
double left = GScheduler.GetRemainTime(timer_id);

/// 取消一个定时器或计划任务
GScheduler.Cancel(timer_id);
  1. Lua接口
scheduler.timer(500, function(id) end);
scheduler.timer(500, function(id) end, true);
scheduler.task(5, 0, 0, function(id) end);
scheduler.is_valid(timer_id);
scheduler.remain(timer_id);
scheduler.cancel(timer_id);

日志

  1. 多线程安全
  2. 日志使用之前可以初始化(不是必要的,但建议初始化—)

日志生成的结构说明

RootOfLogs						指定的日志根目录
|-- 20160803					首先日志会根据“年月日”分文件夹
|	|-- main_01_00_00.000.log	其次日志会按指定大小分文件记录,文件名为指定的"Name_时_分_秒.毫秒.log"
|	|-- main_01_27_18.193.log
/// 初始化日志。日志名为main, 放在logs目录下,输出等级为DEBUG,每个文件最大为4M
Logger::Instance().Initialize("main", "logs", Logger::Debug, 4 * 1024 * 1024);

/// 写日志
LOG_INFO("Hello");
LOG_DEBUG("Hello %d", 2);
LOG_ERR("Error : %s", "Test");
LOG_WARN("You have a warning");

HASH

头文件 Crypto.h

  1. CRC32算法:uint32_t CalcCRC(const char * mem, size_t size, uint32_t pre_crc = 0)
  2. BKDRHash算法:uint32_t CalcHash(const char * mem, size_t size)
  3. MD5算法:class MD5
  4. SHA-1算法:class SHA1
  5. HMAC-SHA1算法:class HMAC_SHA1

工具库

  1. 单例模型:Singleton (singleton.h)
  2. 生存域模型:ScopeGuard (scope_gurad.h)
  3. ZIP压缩/解压缩算法:Zip(zip.h)
  4. 字符串操作:string_tools.h

配置库

  1. CSV文件的读取见:CsvFile(csv.h)
  2. INI文件的读取见:IniFile(ini.h)
  3. JSON文件,使用jsoncpp:Json::Value, Json::Reader (json.h)
  4. 命令行解析: Flags (flags.h)

系统相关

头文件os.h。包含:

  • 高精度时间:
    OS::Tick()
    OS::Now()
    OS::GetTimeZone()
    OS::ParseDataTime(year, month, day, hour, min, sec)

  • 文件系统:
    OS::Exists(path)
    OS::CreateDir(dir)
    OS::GetCWD()
    OS::SetCWD(path)
    OS::GetFullPath(path)
    OS::GetDirName(path)
    OS::GetFileName(path)
    OS::GetFiles(path, recursive)

  • 创建GUID/uuid
    OS::CreateID();

C++
1
https://gitee.com/love_linger/Engine.git
git@gitee.com:love_linger/Engine.git
love_linger
Engine
Engine
master

搜索帮助