1. 程式人生 > >使用開源C++專案WebSocketpp構建web伺服器

使用開源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>