This action will force synchronization from leo/Engine, which will overwrite any changes that you have made since you forked the repository, and can not be recovered!!!
Synchronous operation will process in the background and will refresh the page when finishing processing. Please be patient.
C++服务器编程底层库
项目使用xmake管理,使用方法详见xmake手册
注:Linux下建议使用GCC的
-Wl,-rpath,.
连接选项指定运行期动态连接库的优先查找目录,以方便分发部署
#include <Application.h>
class GameApp : public Application {
public:
GameApp() {}
virtual bool OnInit(const Commandline & cmd) override {
if (cmd.Has("--debug")) {
Logger::Instance().Initialize("game", "logs", ELog::Debug);
} else {
Logger::Instance().Initialize("game", "logs", ELog::Info);
}
/// 锁帧
LockFPS(20);
/// 填写其他初始化逻辑,当返回false时程序直接退出
return true;
}
virtual void OnBreath() override {
/// 这里填写需要每帧更新的逻辑
}
};
RUN_APP(GameApp)
头文件Socket.h
,通常结合IOListener.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;
}
- 设计原则:Lua只负责逻辑,对象生存管理交由C++(可以注册管理到Lua)
- 涉及到Get操作,需要try...catch以捕获类型异常(C++注册到Lua的接口内Get不需要,调用函数不需要)
- Property可以为地址方式,也可以为Getter(TG (void))、Setter(void (TS))方式注册
- 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::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) | 取消一个定时器或计划任务 |
- 由于本人能力有限,经实际效率测试,目前仅保留非线程安全的对象Pool(Pool.h)
- Pool加锁后可用于多线程,但经测试效率还不及系统的new,但Linux下相差不大,如果考虑到无内存碎片的优点,可以自行添加。
- 如果采用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;
}
/// 添加一个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);
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);
日志生成的结构说明
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");
头文件 Crypto.h
uint32_t CalcCRC(const char * mem, size_t size, uint32_t pre_crc = 0)
uint32_t CalcHash(const char * mem, size_t size)
class MD5
class SHA1
class HMAC_SHA1
头文件OS.h
。包含:
高精度时间:
OS::Tick()
OS::Now()
OS::GetTimeZone()
OS::ParseDataTime(year, month, day, hour, min, sec)
文件系统:
OS::Exists(path)
OS::CreateDir(dir)
OS::GetWorkDir()
OS::ChangeWorkDir(path)
OS::GetFullPath(path)
OS::GetDirName(path)
OS::GetFileName(path)
OS::GetFiles(path, recursive)
创建GUID/uuid
OS::CreateID();
Sign in for post a comment
Comments ( 0 )