代码拉取完成,页面将自动刷新
同步操作将从 leo/Engine 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
C++服务器编程底层库
build/Engine.lib
与build/Engined.lib
静态库libengine.so
动态库注:Linux下建议使用GCC的
-Wl,-rpath,XXX
连接选项指定运行期动态连接库的优先查找目录,以方便分发部署
#include <Application.h>
class GameApp : public Application {
public:
GameApp() {}
virtual bool OnInit(Command & rCmd) override {
if (rCmd.Has("--debug")) {
Logger::Instance().Init("game", "logs", 1024 * 1024 * 4, ELog::Debug);
} else {
Logger::Instance().Init("game", "logs", 1024 * 1024 * 4, ELog::Info);
}
/// 锁帧
LockFPS(20);
/// 填写其他初始化逻辑,当返回false时程序直接退出
return true;
}
virtual void OnBreath() override {
/// 这里填写需要每帧更新的逻辑
}
};
RUN_APP(GameApp)
- 头文件 : Network.h
- 客户端继承IConnector,服务器继承IService
- 需要在主线程中调用Breath()来触发一次累计接收信息的处理(如果使用Application类,则不需要)
- Windows平台目的是开发期调试,采用了select模型;Linux下则采用了epoll模型
以客户端为例:
#include <Network.h>
#include <Singleton.h>
#define GServer ServerConnect::Instance()
class ServerConnect : public IConnector, public ISingleton<ServerConnect> {
public:
ServerConnect() {}
...
}
/// 在主线程的初始化
bool GameApp::OnInit(Command & rCmd) {
...
GServer.Connect(...);
...
}
/// 在主线程Breath中调用
void GameApp::OnBreath() {
// Application 模型中不需要手动调用Breath,底层自动调用
// GServer.Breath(); //! 触发一次累计已接收消息的处理。自动回调OnReceive
}
- 设计原则:Lua只负责逻辑,对象生存管理交由C++(可以注册管理到Lua)
- 涉及到Get操作,需要try...catch以捕获类型异常(C++注册到Lua的接口内Get不需要,调用函数不需要)
- Property可以为地址方式,也可以为Getter(T (void))、Setter(void (reference type T))方式注册
- Method必须为
int (*f)(LuaState &)
- 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::nId, 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", 100); // 不需要try
int GetAById(LuaState & r) {
int n = r.Get<int>(1); // 不需要try
...
}
LUA中扩展C++注册的类或名空间(注只能扩展方法,不可扩展属性)
local tbClass = extends("LuaA")
function tbClass:Test()
print("hehe")
end
- 由于本人能力有限,经实际效率测试,目前仅保留非线程安全的对象Pool(Pool.h)
- Pool加锁后可用于多线程,但经测试效率还不及系统的new,但Linux下相差不大,如果考虑到无内存碎片的优点,可以自行添加。
- 如果采用Application的模型,Pool基本上是够用的。因为逻辑主要在主线程的Tick中触发
First. 编写线程内的具体工作类,继承IRunnable.
#include <Runnable.h>
class DemoTask : public IRunnable {
public:
DemoTask(...) { ... } //! 这里为该工作参数初始化
virtual ~DemoTask() { ... } //! 这里为工作结束时清理操作
virtual void Run() { ... } //! 工作的具体内容
private:
... //! 参数声明
};
Second. 对于单一工作的线程池,推荐使用容器Runnable
int main() {
Runnable<DemoTask> iMgr(4); //! 创建含有一个4个工作线程的容器
/// 增加100个并发任务(多余的会暂时等待空闲线程)
for (int i = 0; i < 100; ++i) {
iMgr.Create(...); // 传入工作需要的参数,这里自动调用 new DemoTask(...);
}
/// 等待所有的工作结束,如果不执行该操作,iMgr超出生存期时会放弃未执行的任务。
iMgr.WaitAll();
return 0;
}
Another. 对于多种类型任务共用一个线程池时,请使用ThreadPool.
int main() {
ThreadPool iMgr(4);
/// 添加一种任务
for (int i = 0; i < 50; ++i) {
iMgr.AddRunnable(new DemoTask(...)); //! 请务必使用new,因为当工作结束时会自动调用delete
}
/// 添加另一种任务
for (int j = 0; j < 50; ++j) {
iMgr.AddRunnable(new DemoTask2(...));
}
iMgr.WaitAll();
return 0;
}
日志生成的结构说明
RootOfLogs 指定的日志根目录
|-- 20160803 首先日志会根据“年月日”分文件夹
| |-- main_01_00_00.000.log 其次日志会按指定大小分文件记录,文件名为指定的"Name_时_分_秒.毫秒.log"
| |-- main_01_27_18.193.log
/// 初始化日志。日志名为main, 放在logs目录下,每个文件最大为4M,输出等级为DEBUG,写文件的同时输出到终端
Logger::Instance().Init("main", "logs", 4 * 1024 * 1024, ELog::Debug, true);
/// 写日志
LOG_INFO("Hello");
LOG_DEBUG("Hello %d", 2);
LOG_ERR("Error : %s", "Test");
LOG_WARN("You have a warning");
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。