一個開源的服務器框架,特別適用于開發小游戲
jwEngine
一個跨平臺的c++<->lua服務器快速解決方案,該框架即可快速響應服務器開發工作,設計思想:“讓事情變得更簡單”
網絡底層采用libuv(node.js底層庫),異步io助力使單線程也能釋放澎湃動力,跨平臺支持epoll、iocp、ipv6。框架支持tcp、udp/kcp、websocket、http,并保證了接口的一致性,使用了sol2將所有接口都導出到lua,可以選擇用lua開發邏輯。
使用modern c++開發,盡可能的使用std::move、std::string_view減少內存復制。
該框架使用異步事件,不建議使用多線程,避免多線程上下文切換開銷和破壞代碼美感,網絡部分和邏輯部分使用一個主事件循環驅動。建議的方案是多進程單線程的橫向擴展,按照業務控制各個進程的粒度,當然mysql和redis可以加入到線程池中。
創建一個tcp服務器
只需要簡單幾行代碼即可創建一個tcp高性能服務器,并自動處理數據包頭和粘包(其中包頭包含消息長度和協議號),構建一個完好的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();
- }
創建一個kcp服務器
c++的kcp服務器示例,快速構建你的幀同步服務器,保證消息的可靠性
- 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();
- }
創建一個websocket服務器
自動完成解析websocket協議工作
- 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();
- }
創建一個http服務器
http僅支持簡單的get post請求
- 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()
任意擴展進程節點
你可以任意擴展你的進程,示例:
base進程 | cell進程 | db進程 |
---|---|---|
start engine.exe base.lua | start engine.exe cell.lua | start engine.exe db.lua |
提供一個serialization序列化工具
類似于c++的語法,寫起來非常簡單,示例:
- 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]
- }
- }
通過serialization工具可以將協議的描述文件生成c++和lua代碼,自動生成read()和write()的函數實現,使得數據結構快速映射到SocketBuffer中。
目前serialization序列化工具為實驗性,可能是脆弱的,建議使用更強大的protobuf。該項目已集成lua-protobuf,使得c++和lua之間的協議無縫銜接。
lua-protobuf的開源地址: lua-protobuf
這個示例展示了c++客戶端和lua服務器之間的通訊: 快速搭建服務器Demo
構建
你需要一個modern c++17編譯器
- vs2017 測試通過
- gcc version 9.3.0 測試通過