muduo網路庫學習之muduo_http 庫涉及到的類
1、http request:
request line + header + body (header分為普通報頭,請求報頭與實體報頭)header與body之間有一空行(CRLF)請求方法有:Get, Post, Head, Put, Delete等協議版本1.0、1.1
常用請求頭
Accept:瀏覽器可接受的媒體(MIME)型別;Accept-Language:瀏覽器所希望的語言種類Accept-Encoding:瀏覽器能夠解碼的編碼方法,如gzip,deflate等User-Agent:告訴HTTP伺服器, 客戶端使用的作業系統和瀏覽器的名稱和版本Connection:表示是否需要持久連線,Keep-Alive表示長連線,close表示短連線
GET / HTTP/1.1
Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host: 192.168.159.188:8000
Connection: Keep-Alive
一個典型的http 響應:
Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host: 192.168.159.188:8000
Connection: Keep-Alive
2、http response:
status line + header + body (header分為普通報頭,響應報頭與實體報頭)header與body之間有一空行(CRLF)狀態響應碼
1XX 提示資訊 - 表示請求已被成功接收,繼續處理2XX 成功 - 表示請求已被成功接收,理解,接受3XX 重定向 - 要完成請求必須進行更進一步的處理4XX 客戶端錯誤 - 請求有語法錯誤或請求無法實現5XX 伺服器端錯誤 - 伺服器執行一個有效請求失敗
HTTP/1.1 200 OK
Content-Length: 112
Connection: Keep-Alive
Content-Type: text/html
Server: Muduo
<html><head><title>This is title</title></head><body><h1>Hello</h1>Now is 20130611 02:14:31.518462</body></html>
3、muduo_http 庫
HttpRequest:http請求類封裝,主要有以下幾個成員:
C++ Code
1
2 3 4 5 6 7 8 9 10 11 |
class HttpRequest : public muduo::copyable
{ public: void addHeader(const char *start, const char *colon, const char *end); private: Method method_; // 請求方法 Version version_; // 協議版本1.0/1.1 string path_; // 請求路徑 Timestamp receiveTime_; // 請求時間 std::map<string, string> headers_; // header列表 } |
HttpResponse:http響應類封裝,主要有以下幾個成員:
C++ Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class HttpResponse : public muduo::copyable
{ public: void appendToBuffer(Buffer *output) const; // 將HttpResponse新增到Buffer private: std::map<string, string> headers_; // header列表 HttpStatusCode statusCode_; // 狀態響應碼 // FIXME: add http version string statusMessage_; // 狀態響應碼對應的文字資訊 bool closeConnection_; // 是否關閉連線 string body_; // 實體 }; |
HttpContext:http協議解析類,主要有以下幾個成員:
C++ Code
1
2 3 4 5 6 7 |
class HttpContext : public muduo::copyable { private: HttpRequestParseState state_; // 請求解析狀態 HttpRequest request_; // http請求 }; |
C++ Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/// A simple embeddable HTTP server designed for report status of a program.
/// It is not a fully HTTP 1.1 compliant server, but provides minimum features /// that can communicate with HttpClient and Web browser. /// It is synchronous, just like Java Servlet. class HttpServer : boost::noncopyable { public: typedef boost::function < void (const HttpRequest &, HttpResponse *) > HttpCallback; /// Not thread safe, callback be registered before calling start(). void setHttpCallback(const HttpCallback &cb) { httpCallback_ = cb; } void setThreadNum(int numThreads) { server_.setThreadNum(numThreads); } void start() { server_.start(); } private: void onConnection(const TcpConnectionPtr &conn); void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp receiveTime); void onRequest(const TcpConnectionPtr &, const HttpRequest &); TcpServer server_; HttpCallback httpCallback_; // 在處理http請求(即呼叫onRequest)的過程中回撥此函式,對請求進行具體的處理 }; |
在HttpServer 建構函式中:
C++ Code
1
2 3 4 |
server_.setConnectionCallback(
boost::bind(&HttpServer::onConnection, this, _1)); server_.setMessageCallback( boost::bind(&HttpServer::onMessage, this, _1, _2, _3)); |
即通過設定server_ ,最終設定到TcpConnection 的回撥函式, 當客戶端如瀏覽器連線上來,根據以前的分析可知,呼叫HttpServer::onConnection(), 繫結HttpContext到TcpConnection
中的 boost::any context_;
C++ Code
1
2 3 4 |
if (conn->connected())
{ conn->setContext(HttpContext()); // TcpConnection與一個HttpContext繫結 } |
接著客戶端發出請求,比如訪問伺服器的某個路徑,呼叫HttpServer::onMessage(),
C++ Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void HttpServer::onMessage(const TcpConnectionPtr &conn,
Buffer *buf, Timestamp receiveTime) { HttpContext *context = boost::any_cast<HttpContext>(conn->getMutableContext()); if (!detail::parseRequest(buf, context, receiveTime)) { conn->send("HTTP/1.1 400 Bad Request\r\n\r\n"); conn->shutdown(); } // 請求訊息解析完畢 if (context->gotAll()) { onRequest(conn, context->request()); context->reset(); // 本次請求處理完畢,重置HttpContext,適用於長連線(一個連線多次請求) } } |
其中parseRequest() 會將存放在Buffer 中的請求解析到server_.TcpConnection.context_.request_ 中,最後呼叫HttpServer::onRequest(),
C++ Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void HttpServer::onRequest(const TcpConnectionPtr &conn, const HttpRequest &req)
{ const string &connection = req.getHeader("Connection"); bool close = connection == "close" || (req.getVersion() == HttpRequest::kHttp10 && connection != "Keep-Alive"); HttpResponse response(close); httpCallback_(req, &response); // 客戶程式碼設定的回撥函式,填充response Buffer buf; response.appendToBuffer(&buf); // 將響應填充到buf conn->send(&buf); // 將buf 中的響應傳送給客戶端 if (response.closeConnection()) { conn->shutdown(); //短連線直接關閉 } } |
即要用客戶程式碼設定的httpCallback_ 函式來填充httpResponse,然後傳送給客戶端。
測試程式碼:
HttpServer_test.cc:
C++ Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
#include <muduo/net/http/HttpServer.h>
#include <muduo/net/http/HttpRequest.h> #include <muduo/net/http/HttpResponse.h> #include <muduo/net/EventLoop.h> #include <muduo/base/Logging.h> #include <iostream> #include <map> using namespace muduo; using namespace muduo::net; extern char favicon[555]; bool benchmark = false; // 實際的請求處理 void onRequest(const HttpRequest &req, HttpResponse *resp) { std::cout << "Headers " << req.methodString() << " " << req.path() << std::endl; if (!benchmark) { const std::map<string, string> &headers = req.headers(); for (std::map<string, string>::const_iterator it = headers.begin(); it != headers.end(); ++it) { std::cout << it->first << ": " << it->second << std::endl; } } if (req.path() == "/") { resp->setStatusCode(HttpResponse::k200Ok); resp->setStatusMessage("OK"); resp->setContentType("text/html"); resp->addHeader("Server", "Muduo"); string now = Timestamp::now().toFormattedString(); resp->setBody("<html><head><title>This is title</title></head>" "<body><h1>Hello</h1>Now is " + now + "</body></html>"); } else if (req.path() == "/favicon.ico") { resp->setStatusCode(HttpResponse::k200Ok); resp->setStatusMessage("OK"); resp->setContentType("image/png"); resp->setBody(string(favicon, sizeof favicon)); } else if (req.path() == "/hello") { resp->setStatusCode(HttpResponse::k200Ok); resp->setStatusMessage("OK"); resp->setContentType("text/plain"); resp->addHeader("Server", "Muduo"); resp->setBody("hello, world!\n"); } else { resp->setStatusCode(HttpResponse::k404NotFound); resp->setStatusMessage("Not Found"); resp->setCloseConnection(true); } } int main(int argc, char *argv[]) { int numThreads = 0; if (argc > 1) { benchmark = true; Logger::setLogLevel(Logger::WARN); numThreads = atoi(argv[1]); } EventLoop loop; HttpServer server(&loop, InetAddress(8000), "dummy"); server.setHttpCallback(onRequest); server.setThreadNum(numThreads); server.start(); loop.loop(); } // 這是一個圖片資料 char favicon[555] = { ..... }; |
執行程式,使用瀏覽器訪問目錄,如下:
伺服器端輸出如下:
simba@ubuntu:~/Documents/build/debug/bin$ ./httpserver_test
20131113 08:15:30.110059Z 3637 TRACE IgnoreSigPipe Ignore SIGPIPE - EventLoop.cc:51
20131113 08:15:30.112625Z 3637 TRACE updateChannel fd = 4 events = 3 - EPollPoller.cc:104
20131113 08:15:30.113241Z 3637 TRACE EventLoop EventLoop created 0xBFBC8EF4 in thread 3637 - EventLoop.cc:76
20131113 08:15:30.113677Z 3637 TRACE updateChannel fd = 5 events = 3 - EPollPoller.cc:104
20131113 08:15:30.114767Z 3637 WARN HttpServer[dummy] starts listenning on 0.0.0.0:8000 - HttpServer.cc:155
20131113 08:15:30.115687Z 3637 TRACE updateChannel fd = 6 events = 3 - EPollPoller.cc:104
20131113 08:15:30.116272Z 3637 TRACE loop EventLoop 0xBFBC8EF4 start looping - EventLoop.cc:108
20131113 08:15:32.870784Z 3637 TRACE poll 1 events happended - EPollPoller.cc:65
20131113 08:15:32.871784Z 3637 TRACE printActiveChannels {6: IN } - EventLoop.cc:271
20131113 08:15:32.872287Z 3637 INFO TcpServer::newConnection [dummy] - new connection [dummy:0.0.0.0:8000#1] from 192.168.56.1:2794 - TcpServer.cc:93
20131113 08:15:32.872898Z 3637 DEBUG TcpConnection TcpConnection::ctor[dummy:0.0.0.0:8000#1] at 0x9BD77E0 fd=8 - TcpConnection.cc:65
20131113 08:15:32.873280Z 3637 TRACE newConnection [1] usecount=1 - TcpServer.cc:111
20131113 08:15:32.873640Z 3637 TRACE newConnection [2] usecount=2 - TcpServer.cc:113
20131113 08:15:32.874050Z 3637 TRACE updateChannel fd = 8 events = 3 - EPollPoller.cc:104
20131113 08:15:32.874459Z 3637 TRACE newConnection [5] usecount=2 - TcpServer.cc:123
20131113 08:15:32.890691Z 3637 TRACE poll 1 events happended - EPollPoller.cc:65
20131113 08:15:32.890825Z 3637 TRACE printActiveChannels {8: IN } - EventLoop.cc:271
Headers GET /hello
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Connection: keep-alive
Cookie: Hm_lvt_3d143f0a07b6487f65609d8411e5464f=1380329613
Host: 192.168.56.188:8000
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36
20131113 08:15:42.902273Z 3637 TRACE poll nothing happended - EPollPoller.cc:74
20131113 08:15:52.912887Z 3637 TRACE poll nothing happended - EPollPoller.cc:74
......
20131113 08:17:11.421249Z 3637 TRACE poll 1 events happended - EPollPoller.cc:65
20131113 08:17:11.421328Z 3637 TRACE printActiveChannels {8: IN } - EventLoop.cc:271
Headers GET /favicon.ico
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Connection: keep-alive
Cookie: Hm_lvt_3d143f0a07b6487f65609d8411e5464f=1380329613
Host: 192.168.56.188:8000
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36
20131113 08:17:21.432017Z 3637 TRACE poll nothing happended - EPollPoller.cc:74
20131113 08:17:31.439508Z 3637 TRACE poll nothing happended - EPollPoller.cc:74
20131113 08:15:30.110059Z 3637 TRACE IgnoreSigPipe Ignore SIGPIPE - EventLoop.cc:51
20131113 08:15:30.112625Z 3637 TRACE updateChannel fd = 4 events = 3 - EPollPoller.cc:104
20131113 08:15:30.113241Z 3637 TRACE EventLoop EventLoop created 0xBFBC8EF4 in thread 3637 - EventLoop.cc:76
20131113 08:15:30.113677Z 3637 TRACE updateChannel fd = 5 events = 3 - EPollPoller.cc:104
20131113 08:15:30.114767Z 3637 WARN HttpServer[dummy] starts listenning on 0.0.0.0:8000 - HttpServer.cc:155
20131113 08:15:30.115687Z 3637 TRACE updateChannel fd = 6 events = 3 - EPollPoller.cc:104
20131113 08:15:30.116272Z 3637 TRACE loop EventLoop 0xBFBC8EF4 start looping - EventLoop.cc:108
20131113 08:15:32.870784Z 3637 TRACE poll 1 events happended - EPollPoller.cc:65
20131113 08:15:32.871784Z 3637 TRACE printActiveChannels {6: IN } - EventLoop.cc:271
20131113 08:15:32.872287Z 3637 INFO TcpServer::newConnection [dummy] - new connection [dummy:0.0.0.0:8000#1] from 192.168.56.1:2794 - TcpServer.cc:93
20131113 08:15:32.872898Z 3637 DEBUG TcpConnection TcpConnection::ctor[dummy:0.0.0.0:8000#1] at 0x9BD77E0 fd=8 - TcpConnection.cc:65
20131113 08:15:32.873280Z 3637 TRACE newConnection [1] usecount=1 - TcpServer.cc:111
20131113 08:15:32.873640Z 3637 TRACE newConnection [2] usecount=2 - TcpServer.cc:113
20131113 08:15:32.874050Z 3637 TRACE updateChannel fd = 8 events = 3 - EPollPoller.cc:104
20131113 08:15:32.874459Z 3637 TRACE newConnection [5] usecount=2 - TcpServer.cc:123
20131113 08:15:32.890691Z 3637 TRACE poll 1 events happended - EPollPoller.cc:65
20131113 08:15:32.890825Z 3637 TRACE printActiveChannels {8: IN } - EventLoop.cc:271
Headers GET /hello
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Connection: keep-alive
Cookie: Hm_lvt_3d143f0a07b6487f65609d8411e5464f=1380329613
Host: 192.168.56.188:8000
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36
20131113 08:15:42.902273Z 3637 TRACE poll nothing happended - EPollPoller.cc:74
20131113 08:15:52.912887Z 3637 TRACE poll nothing happended - EPollPoller.cc:74
......
20131113 08:17:11.421249Z 3637 TRACE poll 1 events happended - EPollPoller.cc:65
20131113 08:17:11.421328Z 3637 TRACE printActiveChannels {8: IN } - EventLoop.cc:271
Headers GET /favicon.ico
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Connection: keep-alive
Cookie: Hm_lvt_3d143f0a07b6487f65609d8411e5464f=1380329613
Host: 192.168.56.188:8000
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36
20131113 08:17:21.432017Z 3637 TRACE poll nothing happended - EPollPoller.cc:74
20131113 08:17:31.439508Z 3637 TRACE poll nothing happended - EPollPoller.cc:74
從輸出中看到,fd=6是監聽套接字,fd=8是與伺服器連線起來的已連線套接字(fd=7 是idlefd_)。瀏覽器訪問某個目錄,fd=8可讀事件發生,伺服器端最終執行到httpCallback_(onRequest() 函式),首先從HttpRequest中讀取解析到的請求頭部等資訊並列印出來,即Headers GET /hello 開始的幾行輸出。這是一個長連線,所以第二次切換目錄訪問時,也是fd=8可讀事件發生,只不過填充到HttpResponse
的資料不同,故瀏覽器端看到的輸出就不一樣了。
注意:muduo只實現了最簡單的http應答功能,並不能處理帶有body的http 請求,比如POST。實現http庫主要是為了讓muduo inspect 庫可以通過HTTP方式為伺服器提供監控介面。
參考:
《UNP》
muduo manual.pdf
《linux 多執行緒伺服器程式設計:使用muduo c++網路庫》
相關文章
- muduo網路庫學習之muduo_inspect 庫涉及到的類
- muduo網路庫學習之EventLoop(四):EventLoopThread 類、EventLoopThreadPool 類OOPthread
- muduo網路庫學習筆記(1):Timestamp類筆記
- muduo網路庫學習筆記(3):Thread類筆記thread
- muduo網路庫學習之EventLoop(七):TcpClient、ConnectorOOPTCPclient
- muduo網路庫Timestamp類
- muduo網路庫學習之Timestamp類、AtomicIntegerT 類封裝中的知識點封裝
- muduo網路庫學習之ThreadLocal 類、ThreadLocalSingleton類封裝知識點thread封裝
- muduo網路庫學習之BlockinngQueue類、ThreadPool 類、Singleton類封裝中的知識點BloCthread封裝
- muduo網路庫Exception異常類Exception
- muduo網路庫學習之MutexLock類、MutexLockGuard類、Condition類、CountDownLatch類封裝中的知識點MutexCountDownLatch封裝
- muduo網路庫學習筆記(8):高效日誌類的封裝筆記封裝
- muduo網路庫學習之EventLoop(一):事件迴圈類圖簡介和muduo 定時器TimeQueueOOP事件定時器
- muduo網路庫學習筆記(12):TcpServer和TcpConnection類筆記TCPServer
- muduo網路庫學習筆記(6):單例類(執行緒安全的)筆記單例執行緒
- muduo網路庫學習之Logger類、LogStream類、LogFile類封裝中的知識點封裝
- muduo網路庫學習筆記(2):原子性操作筆記
- muduo網路庫AtomicIntegerT原子整數類
- muduo網路庫學習之EventLoop(六):TcpConnection::send()、shutdown()、handleRead()、handleWrite()OOPTCP
- muduo網路庫學習筆記(11):有用的runInLoop()函式筆記OOP函式
- muduo網路庫學習筆記(14):chargen服務示例筆記
- muduo網路庫學習筆記(13):TcpConnection生命期的管理筆記TCP
- muduo網路庫學習筆記(10):定時器的實現筆記定時器
- muduo網路庫學習之Exception類、Thread 類封裝中的知識點(重點講pthread_atfork())Exceptionthread封裝
- muduo網路庫使用心得
- muduo網路庫學習筆記(9):Reactor模式的關鍵結構筆記React模式
- muduo網路庫學習筆記(5):執行緒池的實現筆記執行緒
- muduo網路庫學習筆記(7):執行緒特定資料筆記執行緒
- muduo網路庫學習之EventLoop(二):程式(執行緒)wait/notify 和 EventLoop::runInLoopOOP執行緒AI
- muduo網路庫學習之EventLoop(五):TcpConnection生存期管理(連線關閉)OOPTCP
- muduo網路庫編譯安裝編譯
- muduo網路庫學習筆記(4):互斥量和條件變數筆記變數
- muduo網路庫學習之EventLoop(三):Socket、Acceptor、TcpServer、TcpConnection(連線建立,接收訊息)OOPTCPServer
- muduo網路庫學習筆記(15):關於使用stdio和iostream的討論筆記iOS
- muduo網路庫學習筆記(0):物件導向程式設計風格和基於物件程式設計風格的比較筆記物件程式設計
- 13封裝網路請求類庫封裝
- 資料庫學習線路圖資料庫
- 網路資料庫練習題資料庫