1. 程式人生 > >高效能C++網路庫libtnet實現:http

高效能C++網路庫libtnet實現:http

HTTP

libtnet提供了簡單的http支援,使用也很簡單。

一個簡單的http server:

void onHandler(const HttpConnectionPtr_t& conn, const HttpRequest& request)
{
    HttpResponse resp;
    resp.statusCode = 200;
    resp.setContentType("text/html");
    resp.body.append("Hello World");    
    conn->send(resp);
}

TcpServer s;
HttpServer httpd(&s);
httpd.setHttpCallback("/test", std::bind(&onHandler, _1, _2));
httpd.listen(Address(80));
s.start(4);

我們對http server的"/test"註冊了一個handler,當用戶訪問該url的時候,就會顯示"Hello World"。

同樣,http client的使用也很簡單:

void onResponse(IOLoop* loop, const HttpResponse& resp)
{
    cout << resp.body << endl;
    loop->stop();
}

IOLoop loop;
HttpClientPtr_t client = std::make_shared<HttpClient>(&loop);
client->request("http://127.0.0.1:80/test", std::bind(&onResponse, &loop, _1));
loop.start(); 

這裡,我們使用了一個http client,向server請求"/test"的內容,當伺服器有響應之後,會呼叫響應的回撥函式。

HTTP Parser

對於http的解析,我採用的是http parser,因為它採用的是流式解析,同時非常容易整合進libtnet。

使用http parser只需要設定相應的回撥函式即可。http parser有如下幾種回撥:

  • message begin,解析開始的時候呼叫
  • url,解析url的時候呼叫
  • status complete,http response解析status的時候呼叫
  • header field,解析http header的field呼叫
  • header value,解析http header的value呼叫
  • headers complete,解析完成http header呼叫
  • body,解析http body呼叫
  • message complete,解析完成呼叫

這裡特別需要注意的是http header的解析,因為http parser將其拆分成了兩種回撥,所以我們在處理的時候需要記錄上一次header callback是field的還是value的。在解析field的時候,如果上一次是value callback,那我們就需要將上一次解析的field和value儲存下來,而該次的解析則是一個新的field了。

另外,http parser還提供了upgrade的支援,所以我們很方便的就能區分該次請求是否為websocket。

Websocket

libtnet也提供了websocket的支援,現階段,只支援RFC6455

當libtnet通過http parser發現該次請求為websocket的時候,就進入了websocket的流程。websocket的使用也很簡單,當握手成功之後,後續的所有通訊就是純粹的tcp通訊了。

一個簡單的websocket server:

void onWsCallback(const WsConnectionPtr_t& conn, WsEvent event, const void* context)
{
    switch(event)
    {
        case Ws_CloseEvent:
            break;
        case Ws_MessageEvent:
            {
                const string& str = *(const string*)context;
                conn->send(str);
            }
            break;
        case Ws_PongEvent:
            break;
        default:
            break;
    }
}

TcpServer s;
HttpServer httpd(&s);
httpd.setWsCallback("/push/ws", std::bind(&onWsCallback, _1, _2, _3));    
httpd.listen(Address(80));
s.start();

可以看到,websocket的callback機制也類似於libtnet connection的callback機制,使用者需通過event + context的方式來處理該次回撥的資料。

libtnet對websocket的frame的處理參照的是tornado的websocket模組。也能夠組合多frame的資料,外部只需要關注Ws_MessageEvent即可。

websocket client的實現也很簡單:

void onWsConnEvent(const WsConnectionPtr_t& conn, WsEvent event, const void* context)
{
    switch(event)
    {
        case Ws_OpenEvent:
            conn->send("Hello world");
            break;    
        case Ws_MessageEvent:
            {
                const string& msg = *(const string*)context;

                LOG_INFO("message %s", msg.c_str());
                conn->close();                        
            }
            break;
        default:
            break;
    }    
}

IOLoop loop;
WsClientPtr_t client = std::make_shared<WsClient>(&loop);    
client->connect("ws://127.0.0.1:80/push/ws", std::bind(&onWsConnEvent, _1, _2, _3));
loop.start();

libtnet的地址https://github.com/siddontang/libtnet,歡迎圍觀。