使用開源C++專案WebSocketpp構建web伺服器
1、WebsocketPP簡介
最近需要構建一個本地的伺服器程式,處理PHP伺服器發來的請求,看到微軟的一個開源專案內部用到了這個開源庫,於是就試著用了下,效果還好,主要是很容易處理Web請求並返回資料。(C++寫程式來請求,然後用C++在WebsocketPP構建伺服器處理請求,想怎麼定義資料怎麼加密都可以,因為都是C++程式,也不用學習PHP,想想就很美好)。
WebsocketPP是一個使用C++編寫的開源Web伺服器框架,具體實現則是用的大名鼎鼎的boost::asio。ASIO是一個跨平臺的網路庫,Windows上底層實現使用的是重疊I/O(PS:Windows上socket伺服器效能最好的當然是IOCP了),其架構設計如下圖:
2、使用WebsocketPP
去Github上把壓縮包下載下來,裡面很多例子可以參考,因為涉及到很多型別(伺服器、客戶端、請求、返回……)。我也就用了下處理web請求,返回Json資料。
VS新建一個工程,然後把websocketpp這個資料夾都拷貝過去,把所有檔案都新增到VS中。新增標頭檔案,建立一個擴充套件的伺服器類……看程式碼
#include "stdafx.h" #include <websocketpp/config/asio_no_tls.hpp> #include <websocketpp/server.hpp> #include <iostream> using std::string; using std::wstring; struct testee_config : public websocketpp::config::asio { // pull default settings from our core config typedef websocketpp::config::asio core; typedef core::concurrency_type concurrency_type; typedef core::request_type request_type; typedef core::response_type response_type; typedef core::message_type message_type; typedef core::con_msg_manager_type con_msg_manager_type; typedef core::endpoint_msg_manager_type endpoint_msg_manager_type; typedef core::alog_type alog_type; typedef core::elog_type elog_type; typedef core::rng_type rng_type; typedef core::endpoint_base endpoint_base; static bool const enable_multithreading = true; struct transport_config : public core::transport_config { typedef core::concurrency_type concurrency_type; typedef core::elog_type elog_type; typedef core::alog_type alog_type; typedef core::request_type request_type; typedef core::response_type response_type; static bool const enable_multithreading = true; }; typedef websocketpp::transport::asio::endpoint<transport_config> transport_type; static const websocketpp::log::level elog_level = websocketpp::log::elevel::all; static const websocketpp::log::level alog_level = websocketpp::log::alevel::all; }; typedef websocketpp::server<testee_config> server; using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; // pull out the type of messages sent by our config typedef server::message_ptr message_ptr; // Define a callback to handle incoming messages void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { s->send(hdl, msg->get_payload(), msg->get_opcode()); } void on_socket_init(websocketpp::connection_hdl, boost::asio::ip::tcp::socket & s) { boost::asio::ip::tcp::no_delay option(true); s.set_option(option); } void on_http(server* s, websocketpp::connection_hdl hdl) { server::connection_ptr con = s->get_con_from_hdl(hdl); websocketpp::http::parser::request rt = con->get_request(); const string& strUri = rt.get_uri(); const string& strMethod = rt.get_method(); const string& strBody = rt.get_body(); const string& strVersion = rt.get_version(); std::cout<<"接收到一個"<<strMethod.c_str()<<"請求:"<<strUri.c_str()<<"執行緒ID="<<::GetCurrentThreadId()<<std::endl; con->set_body("everything is ok now!"); con->set_status(websocketpp::http::status_code::value(600));//websocketpp::http::status_code::ok websocketpp::http::parser::response rp; string strContent = rt.raw(); rp.consume(strContent.c_str(), strContent.size()); //if ( strMethod.compare("POST") == 0 ) { websocketpp::http::parser::request r; size_t nRet = r.consume(strUri.c_str(), strUri.size()); int k = 0; } } int main(int argc, char * argv[]) { // Create a server endpoint // websocketpp::http::parser::request r; // r.set_uri("http://www.baidu.com"); // r.set_method("GET"); // r.set_version("HTTP/1.1"); // r.set_body("/?s=123"); // string str = r.raw(); // std::string firefox = "GET / HTTP/1.1\r\nHost: localhost:5000\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0) Gecko/20100101 Firefox/10.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive, Upgrade\r\nSec-WebSocket-Version: 8\r\nSec-WebSocket-Origin: http://zaphoyd.com\r\nSec-WebSocket-Key: pFik//FxwFk0riN4ZiPFjQ==\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nUpgrade: websocket\r\n\r\n"; // // size_t n = r.consume(firefox.c_str(), firefox.size()); server testee_server; short port = 1990; size_t num_threads = 1; if (argc == 3) { port = atoi(argv[1]); num_threads = atoi(argv[2]); } try { // Total silence testee_server.clear_access_channels(websocketpp::log::alevel::all); testee_server.clear_error_channels(websocketpp::log::alevel::all); // Initialize ASIO testee_server.init_asio(); testee_server.set_reuse_addr(true); // Register our message handler testee_server.set_message_handler(bind(&on_message,&testee_server,::_1,::_2)); testee_server.set_socket_init_handler(bind(&on_socket_init,::_1,::_2)); testee_server.set_http_handler(bind(&on_http, &testee_server, ::_1)); // Listen on specified port with extended listen backlog testee_server.set_listen_backlog(8192); testee_server.listen(port); // Start the server accept loop testee_server.start_accept(); // Start the ASIO io_service run loop if (num_threads == 1) { testee_server.run(); } else { typedef websocketpp::lib::shared_ptr<websocketpp::lib::thread> thread_ptr; std::vector<thread_ptr> ts; for (size_t i = 0; i < num_threads; i++) { ts.push_back(websocketpp::lib::make_shared<websocketpp::lib::thread>(&server::run, &testee_server)); } for (size_t i = 0; i < num_threads; i++) { ts[i]->join(); } } } catch (websocketpp::exception const & e) { std::cout << "exception: " << e.what() << std::endl; } }
</pre><pre name="code" class="cpp">
on_http就是處理web請求的回撥,在這裡我們可以根據請求名稱來響應處理。const string& strUri = rt.get_uri();獲取的就是請求的名稱,比如設定伺服器埠號為1200,請求:localhost:1200/hello,那麼strUri就是/hello,也就是說我們可以根據字串的匹配來處理不同的請求名稱。還有就是POST/GET請求,const string& strMethod = rt.get_method()獲取的就是請求方法,POST對應的strMethod
=“POST”,GET對應的就是"GET"。請求的資料,const string& strBody = rt.get_body(),只有POST請求才可能有傳送的資料,GET請求時這裡為空。const string& strVersion = rt.get_version()獲取的是HTTP的版本號,通常是1.1。
con->set_body("everything is ok now!");con->set_status(websocketpp::http::status_code::value(600));這兩句是設定返回資料和返回碼。
設定伺服器監聽埠testee_server.listen(port),埠號被佔用時,開啟伺服器會失敗,如何捕捉呢?在catch (websocketpp::exception const & e)中。
吐槽下CSDN的文章編輯器,真是垃圾。格式設定好痛苦,我複製文字後得貼上到記事本里去掉格式然後再複製貼上回來。
</pre><p></p><p></p><p></p><pre>
</pre><pre>