背景
新的c++11標准出后,c++語法得到了非常多的擴展,比起以往不論什么時候都要靈活和高效,提高了程序編碼的效率,為軟件開發者節省了不少的時間。
之前我也寫過基於ACE的網絡server框架,但ACE畢竟有些臃腫。內部對象關系錯綜復雜,easy給人造成僅僅見樹木不見森林的錯覺。 所以打算用c++11開發一個較為簡潔,高效,支持高並發的網絡庫。
開源
基礎的結構已經開發完畢,代碼也開源在github上。網址是 https://github.com/lichuan/fly 歡迎各位提出建議。
結構
fly網絡庫主要分為base模塊,task模塊,net模塊。base主要是一些最基礎的功能集合。包含日志。id分配器,隨機數,隊列等。task主要封裝了任務抽象以及任務調度運行等功能;net主要是實現網絡層面的封裝。為上層使用者提供簡單易用的網絡接口,這也是fly庫的核心模塊。實現了解析協議的Parser封裝,負責監聽網絡的Acceptor,負責網絡連接io功能的Poller封裝體,在Parser、Acceptor、和Poller的基礎上還進一步封裝了作為server來使用的Server類和作為client來使用的Client類,這兩個類是fly庫的核心類,上層的應用能夠直接使用Server對象和Client對象來建立網絡server。作為網絡連接概念的Connection對象則是通過std::shared_ptr來管理它的生命周期的,shared_ptr內置共享對象的功能大大地簡化了網絡連接生命期的管理。
協議
fly庫的網絡協議基於全世界最通用的json格式。而它的解析則依賴於rapidjson第三方庫來完畢。
協議組成例如以下:
|長度字段|{協議類型:類型值,協議命令:命令值,其它json字段}|
協議類型和協議命令組成了二級消息字,就能夠組合出各種約定的協議了。
應用
fly庫的test文件夾提供了一個簡單的例程。當中test_server.cpp代碼例如以下:
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * _______ _ * * ( ____ \ ( \ |\ /| * * | ( \/ | ( ( \ / ) * * | (__ | | \ (_) / * * | __) | | \ / * * | ( | | ) ( * * | ) | (____/\ | | * * |/ (_______/ \_/ * * * * * * fly is an awesome c++11 network library. * * * * @author: lichuan * * @qq: 308831759 * * @email: 308831759@qq.com * * @github: https://github.com/lichuan/fly * * @date: 2015-06-10 13:34:21 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include <unistd.h> #include <unordered_map> #include "fly/init.hpp" #include "fly/net/server.hpp" #include "fly/base/logger.hpp" using namespace std::placeholders; class Test_Server : public fly::base::Singleton<Test_Server> { public: bool allow(std::shared_ptr<fly::net::Connection> connection) { return true; } void init(std::shared_ptr<fly::net::Connection> connection) { std::lock_guard<std::mutex> guard(m_mutex); m_connections[connection->id()] = connection; LOG_INFO("connection count: %u", m_connections.size()); } void dispatch(std::unique_ptr<fly::net::Message> message) { std::shared_ptr<fly::net::Connection> connection = message->get_connection(); const fly::net::Addr &addr = connection->peer_addr(); LOG_INFO("recv message from %s:%d raw_data: %s", addr.m_host.c_str(), addr.m_port, message->raw_data().c_str()); } void close(std::shared_ptr<fly::net::Connection> connection) { LOG_INFO("close connection from %s:%d", connection->peer_addr().m_host.c_str(), connection->peer_addr().m_port); std::lock_guard<std::mutex> guard(m_mutex); m_connections.erase(connection->id()); LOG_INFO("connection count: %u", m_connections.size()); } void be_closed(std::shared_ptr<fly::net::Connection> connection) { LOG_INFO("connection from %s:%d be closed", connection->peer_addr().m_host.c_str(), connection->peer_addr().m_port); std::lock_guard<std::mutex> guard(m_mutex); m_connections.erase(connection->id()); LOG_INFO("connection count: %u", m_connections.size()); } void main() { //init library fly::init(); //init logger fly::base::Logger::instance()->init(fly::base::DEBUG, "server", "./log/"); //test tcp server std::unique_ptr<fly::net::Server> server(new fly::net::Server(fly::net::Addr("127.0.0.1", 8899), std::bind(&Test_Server::allow, this, _1), std::bind(&Test_Server::init, this, _1), std::bind(&Test_Server::dispatch, this, _1), std::bind(&Test_Server::close, this, _1), std::bind(&Test_Server::be_closed, this, _1))); if(server->start()) { LOG_INFO("start server ok!"); server->wait(); } else { LOG_ERROR("start server failed"); } } private: std::unordered_map<uint64, std::shared_ptr<fly::net::Connection>> m_connections; std::mutex m_mutex; }; int main() { Test_Server::instance()->main(); }
Server對象構造時會要求傳入監聽地址和回調函數,當Server對象start啟動時,fly庫底層就會建立對應的Poller、Parser、Acceptor對象,假設想實現多線程Poller和Parser,則需傳入並發線程數量就可以,回調函數說明例如以下:
allow_cb:當有新的連接到達時調用。來推斷是否同意該連接的注冊。
init_cb:當把連接對象注冊到某一個Poller和Parser后調用,進行初始化處理。
dispatch_cb:當有消息到達時會調用,進行消息派發。
close_cb:主動關閉連接對象時調用。
be_closed_cb:檢測到對端關閉連接對象時調用。
test_client.cpp主要使用Client對象來連接到某一個server,相同Client構造時也須要傳入回調函數,其作用與Server構造時傳入的回調一樣。