一個(gè)開(kāi)源的服務(wù)器框架,特別適用于開(kāi)發(fā)小游戲
jwEngine
一個(gè)跨平臺(tái)的c++<->lua服務(wù)器快速解決方案,該框架即可快速響應(yīng)服務(wù)器開(kāi)發(fā)工作,設(shè)計(jì)思想:“讓事情變得更簡(jiǎn)單”
網(wǎng)絡(luò)底層采用libuv(node.js底層庫(kù)),異步io助力使單線程也能釋放澎湃動(dòng)力,跨平臺(tái)支持epoll、iocp、ipv6??蚣苤С謙cp、udp/kcp、websocket、http,并保證了接口的一致性,使用了sol2將所有接口都導(dǎo)出到lua,可以選擇用lua開(kāi)發(fā)邏輯。
使用modern c++開(kāi)發(fā),盡可能的使用std::move、std::string_view減少內(nèi)存復(fù)制。
該框架使用異步事件,不建議使用多線程,避免多線程上下文切換開(kāi)銷和破壞代碼美感,網(wǎng)絡(luò)部分和邏輯部分使用一個(gè)主事件循環(huán)驅(qū)動(dòng)。建議的方案是多進(jìn)程單線程的橫向擴(kuò)展,按照業(yè)務(wù)控制各個(gè)進(jìn)程的粒度,當(dāng)然mysql和redis可以加入到線程池中。
創(chuàng)建一個(gè)tcp服務(wù)器
只需要簡(jiǎn)單幾行代碼即可創(chuàng)建一個(gè)tcp高性能服務(wù)器,并自動(dòng)處理數(shù)據(jù)包頭和粘包(其中包頭包含消息長(zhǎng)度和協(xié)議號(hào)),構(gòu)建一個(gè)完好的NetPacket交給你。
- class INetEvent : public NetEvent
- {
- public:
- virtual void onAccept(NetConnect * conn){}
- virtual void onClose(NetConnect * conn){}
- virtual void onMsg(NetConnect * conn, int msgtype, NetPacket * pack){}
- };
- int main()
- {
- EventLoop::Instance()->init();
- INetEvent eve;
- NetServer server(EventLoop::Instance(), &eve);
- server.listen("127.0.0.1", 3001);
- return EventLoop::Instance()->run();
- }
創(chuàng)建一個(gè)kcp服務(wù)器
c++的kcp服務(wù)器示例,快速構(gòu)建你的幀同步服務(wù)器,保證消息的可靠性
- class KNetEvent : public KcpEvent
- {
- public:
- virtual void onAccept(KcpSession * conn){};
- virtual void onClose(KcpSession * conn){};
- virtual void onMsg(KcpSession * conn, int msgtype, UdpPacket * pack){}
- virtual void onUdpTimeout(KcpSession * s){}
- };
- int main()
- {
- EventLoop::Instance()->init();
- KNetEvent eve;
- KcpServer server(EventLoop::Instance(), &eve);
- server.start("127.0.0.1", 3001);
- return EventLoop::Instance()->run();
- }
創(chuàng)建一個(gè)websocket服務(wù)器
自動(dòng)完成解析websocket協(xié)議工作
- class IWebEvent : public WebSocketEvent
- {
- public:
- virtual void onHandshake(WebSocketConnect * conn){};
- virtual void onAccept(WebSocketConnect * conn){};
- virtual void onClose(WebSocketConnect * conn){};
- virtual void onMsg(WebSocketConnect * conn, WebSocketPacket * pack){};
- };
- int main()
- {
- EventLoop::Instance()->init();
- IWebEvent wevent;
- WebSocketServer server(EventLoop::Instance(), &wevent);
- server.listen("127.0.0.1", 8080);
- return EventLoop::Instance()->run();
- }
創(chuàng)建一個(gè)http服務(wù)器
http僅支持簡(jiǎn)單的get post請(qǐng)求
- const char * html = R"(<html>
- <body>
- <h1>login</h1>
- <p>hello world!</p>
- <form action="login" method="post">
- <input type="text" name="user"/>
- <input type="password" name="pass"/>
- <input type="submit" value="login"/>
- </form>
- </body>
- </html>)";
- const char * succeed = ""
- "<html>"
- "<body>"
- "<h1>login succeed</h1>"
- "</body>"
- "</html>";
- const char * failing = ""
- "<html>"
- "<body>"
- "<h1>login failing</h1>"
- "</body>"
- "</html>";
- int main()
- {
- EventLoop::Instance()->init();
- HttpServer server(EventLoop::Instance());
- server.listen("127.0.0.1", 80);
- server.addGet("/", [](HttpConnect *conn, std::string_view & data) {
- conn->autoMsg(html);
- });
- server.addPost("/login", [](HttpConnect *conn, std::string_view & data) {
- HttpParam hp(data);
- if (hp.getStr("user") == "jw" && hp.getStr("pass") == "1111")
- {
- conn->autoMsg(succeed);
- }
- else
- {
- conn->autoMsg(failing);
- }
- });
- return EventLoop::Instance()->run();
- }
mysql和線程池
這次我們用lua示例:
- local config = DBConfig:new()
- config.device = "mysql"
- config.ip = "127.0.0.1"
- config.dbname = "jw_test"
- config.user = "root"
- config.pswd = "1111"
- config.port = 3306
- pool = DBThreadPool:new(config)
- pool:create(1)
- func = function(err, result)
- while(result:fetch())
- do
- local id = result:getInt32()
- local num = result:getInt32()
- local name = result:getString()
- local str = "id:" .. id .. ", num:" .. num .. ", name:" .. name
- print(str)
- end
- end
- function exec()
- local sql = SqlCommand:new("select * from test where id = ?")
- sql:pushInt32(1)
- sql:addToPool(pool, func)
- end
- event_init()
- exec()
- timer = UTimer:new()
- timer:start(function ()
- pool:update()
- end, 10, 10)
- event_run()
任意擴(kuò)展進(jìn)程節(jié)點(diǎn)
你可以任意擴(kuò)展你的進(jìn)程,示例:
base進(jìn)程 | cell進(jìn)程 | db進(jìn)程 |
---|---|---|
start engine.exe base.lua | start engine.exe cell.lua | start engine.exe db.lua |
提供一個(gè)serialization序列化工具
類似于c++的語(yǔ)法,寫起來(lái)非常簡(jiǎn)單,示例:
- struct testmsg
- {
- int32 x
- int32 y
- int32 z
- int8 state
- vector<int32> vec
- read{
- [x, y, z, state]
- if(state == 1)
- {
- [vec]
- }
- }
- write{
- [x, y, z, state, vec]
- }
- }
通過(guò)serialization工具可以將協(xié)議的描述文件生成c++和lua代碼,自動(dòng)生成read()和write()的函數(shù)實(shí)現(xiàn),使得數(shù)據(jù)結(jié)構(gòu)快速映射到SocketBuffer中。
目前serialization序列化工具為實(shí)驗(yàn)性,可能是脆弱的,建議使用更強(qiáng)大的protobuf。該項(xiàng)目已集成lua-protobuf,使得c++和lua之間的協(xié)議無(wú)縫銜接。
lua-protobuf的開(kāi)源地址: lua-protobuf
這個(gè)示例展示了c++客戶端和lua服務(wù)器之間的通訊: 快速搭建服務(wù)器Demo
構(gòu)建
你需要一個(gè)modern c++17編譯器
- vs2017 測(cè)試通過(guò)
- gcc version 9.3.0 測(cè)試通過(guò)