ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

muduo学习笔记:net部分之Http--HttpServer

2021-09-06 22:05:02  阅读:243  来源: 互联网

标签:muduo Http -- 0.0 resp TcpServer HttpServer const x0


前面【muduo学习笔记:net部分之Http–HttpRequest、HttpResponse 和 HttpContext】介绍了TCP数据数据Buffer承载的HTTP报文的解析,本文结合TcpServer,基于muduo实现一个简单的HttpServer。由于对协议解析不完善,它不适合写web的通用服务。

在这里插入图片描述

1、HttpServer的定义

有了TcpServer的基础,加上前面博客关于HttpRequest、HttpResponse 和 HttpContext的使用,我们可以封装一个HttpServer,在收到客户端的消息后,按照HTTP协议解析Buffer,并使用回调函供用户处理Request、返回Response。

class HttpServer : noncopyable
{
 public:
  typedef std::function<void (const HttpRequest&, HttpResponse*)> HttpCallback;

  HttpServer(EventLoop* loop,
             const InetAddress& listenAddr,
             const string& name,
             TcpServer::Option option = TcpServer::kNoReusePort);

  EventLoop* getLoop() const { return server_.getLoop(); }

  /// Not thread safe, callback be registered before calling start().
  // 设置http请求的回调函数
  void setHttpCallback(const HttpCallback& cb) { httpCallback_ = cb; }

  void setThreadNum(int numThreads) { server_.setThreadNum(numThreads); }

  void start();

 private:
  // TcpServer的新连接、新消息的回调函数
  void onConnection(const TcpConnectionPtr& conn);
  void onMessage(const TcpConnectionPtr& conn, Buffer* buf,Timestamp receiveTime);
  
  // 在onMessage中调用,并调用用户注册的httpCallback_函数,对请求进行具体的处理
  void onRequest(const TcpConnectionPtr&, const HttpRequest&); 

  TcpServer server_;
  HttpCallback httpCallback_;
};

2、HttpServer的实现

在HttpServer构造函数中,注册TcpServer对外暴露的两个回调函数:新的连接、新消息到来。

  server_.setConnectionCallback(std::bind(&HttpServer::onConnection, this, _1));
  server_.setMessageCallback(std::bind(&HttpServer::onMessage, this, _1, _2, _3));

当客户端如浏览器连接上来,根据以前的分析可知,调用HttpServer::onConnection(), 绑定一个HttpContextTcpConnection 中的成员变量std::any context_,这里绑定一个HttpContext主要是为了长连接中仅分配一次对象,提高效率。

void HttpServer::onConnection(const TcpConnectionPtr& conn)
{
  if (conn->connected()){
    conn->setContext(HttpContext());  // 绑定一个HttpContext到TcpConnection
  }
}

接着客户端发出请求,比如访问服务器的某个路径,那么自动回调HttpServer::onMessage(),

void HttpServer::onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)
{
  HttpContext* context = std::any_cast<HttpContext>(conn->getMutableContext());
  // 解析请求
  if (!context->parseRequest(buf, receiveTime)){  
    conn->send("HTTP/1.1 400 Bad Request\r\n\r\n");  // 解析失败, 400错误
    conn->shutdown();   // 断开本段写
  }
  // 请求解析成功
  if (context->gotAll()){
    onRequest(conn, context->request());  // 调用onRequest()私有函数
    context->reset();					  // 复用HttpContext对象
  }
}

其中parseRequest() 会将存放在Buffer 中的请求解析到server_.TcpConnection.context_.request_ 中,最后调用HttpServer::onRequest(),

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);  // 调用客户端的http处理函数,填充response
  Buffer buf;
  response.appendToBuffer(&buf); // 将响应格式化填充到buf中
  conn->send(&buf);				 // 将响应回复发送给客户端
  if (response.closeConnection()){
    conn->shutdown();  // 如果是短连接,直接关闭。
  }
}

即要用客户代码设置的httpCallback_ 函数来填充HttpResponse,然后发送给客户端。

3、HttpServer测试

muduo中自带的sample实现了几个GET请求,POST的请求可以根据实际情况进行解析。

#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];    // favicon图标数据
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 (const auto& header : headers){
      std::cout << header.first << ": " << header.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")  // /hello,返回文本 
  {
    resp->setStatusCode(HttpResponse::k200Ok);
    resp->setStatusMessage("OK");
    resp->setContentType("text/plain");
    resp->addHeader("Server", "Muduo");
    resp->setBody("hello, world!\n");
  }
  else  // 不存在的URL,404 错误
  {
    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] = {
  '\x89', 'P', 'N', 'G', '\xD', '\xA', '\x1A', '\xA','\x0', '\x0', '\x0', '\xD', 'I', 'H', 'D', 'R',
  '\x0', '\x0', '\x0', '\x10', '\x0', '\x0', '\x0', '\x10','\x8', '\x6', '\x0', '\x0', '\x0', '\x1F', '\xF3', '\xFF',
  'a', '\x0', '\x0', '\x0', '\x19', 't', 'E', 'X','t', 'S', 'o', 'f', 't', 'w', 'a', 'r',
  'e', '\x0', 'A', 'd', 'o', 'b', 'e', '\x20','I', 'm', 'a', 'g', 'e', 'R', 'e', 'a',
  'd', 'y', 'q', '\xC9', 'e', '\x3C', '\x0', '\x0',  '\x1', '\xCD', 'I', 'D', 'A', 'T', 'x', '\xDA',
  '\x94', '\x93', '9', 'H', '\x3', 'A', '\x14', '\x86',  '\xFF', '\x5D', 'b', '\xA7', '\x4', 'R', '\xC4', 'm',
  '\x22', '\x1E', '\xA0', 'F', '\x24', '\x8', '\x16', '\x16',  'v', '\xA', '6', '\xBA', 'J', '\x9A', '\x80', '\x8',
  'A', '\xB4', 'q', '\x85', 'X', '\x89', 'G', '\xB0',  'I', '\xA9', 'Q', '\x24', '\xCD', '\xA6', '\x8', '\xA4',
  'H', 'c', '\x91', 'B', '\xB', '\xAF', 'V', '\xC1',  'F', '\xB4', '\x15', '\xCF', '\x22', 'X', '\x98', '\xB',
  'T', 'H', '\x8A', 'd', '\x93', '\x8D', '\xFB', 'F',  'g', '\xC9', '\x1A', '\x14', '\x7D', '\xF0', 'f', 'v',
  'f', '\xDF', '\x7C', '\xEF', '\xE7', 'g', 'F', '\xA8',  '\xD5', 'j', 'H', '\x24', '\x12', '\x2A', '\x0', '\x5',
  '\xBF', 'G', '\xD4', '\xEF', '\xF7', '\x2F', '6', '\xEC',  '\x12', '\x20', '\x1E', '\x8F', '\xD7', '\xAA', '\xD5', '\xEA',
  '\xAF', 'I', '5', 'F', '\xAA', 'T', '\x5F', '\x9F',  '\x22', 'A', '\x2A', '\x95', '\xA', '\x83', '\xE5', 'r',
  '9', 'd', '\xB3', 'Y', '\x96', '\x99', 'L', '\x6',  '\xE9', 't', '\x9A', '\x25', '\x85', '\x2C', '\xCB', 'T',
  '\xA7', '\xC4', 'b', '1', '\xB5', '\x5E', '\x0', '\x3',  'h', '\x9A', '\xC6', '\x16', '\x82', '\x20', 'X', 'R',
  '\x14', 'E', '6', 'S', '\x94', '\xCB', 'e', 'x',  '\xBD', '\x5E', '\xAA', 'U', 'T', '\x23', 'L', '\xC0',
  '\xE0', '\xE2', '\xC1', '\x8F', '\x0', '\x9E', '\xBC', '\x9',  'A', '\x7C', '\x3E', '\x1F', '\x83', 'D', '\x22', '\x11',
  '\xD5', 'T', '\x40', '\x3F', '8', '\x80', 'w', '\xE5',  '3', '\x7', '\xB8', '\x5C', '\x2E', 'H', '\x92', '\x4',
  '\x87', '\xC3', '\x81', '\x40', '\x20', '\x40', 'g', '\x98',  '\xE9', '6', '\x1A', '\xA6', 'g', '\x15', '\x4', '\xE3',
  '\xD7', '\xC8', '\xBD', '\x15', '\xE1', 'i', '\xB7', 'C',  '\xAB', '\xEA', 'x', '\x2F', 'j', 'X', '\x92', '\xBB',
  '\x18', '\x20', '\x9F', '\xCF', '3', '\xC3', '\xB8', '\xE9',  'N', '\xA7', '\xD3', 'l', 'J', '\x0', 'i', '6',
  '\x7C', '\x8E', '\xE1', '\xFE', 'V', '\x84', '\xE7', '\x3C',  '\x9F', 'r', '\x2B', '\x3A', 'B', '\x7B', '7', 'f',
  'w', '\xAE', '\x8E', '\xE', '\xF3', '\xBD', 'R', '\xA9',  'd', '\x2', 'B', '\xAF', '\x85', '2', 'f', 'F',
  '\xBA', '\xC', '\xD9', '\x9F', '\x1D', '\x9A', 'l', '\x22',  '\xE6', '\xC7', '\x3A', '\x2C', '\x80', '\xEF', '\xC1', '\x15',
  '\x90', '\x7', '\x93', '\xA2', '\x28', '\xA0', 'S', 'j',  '\xB1', '\xB8', '\xDF', '\x29', '5', 'C', '\xE', '\x3F',
  'X', '\xFC', '\x98', '\xDA', 'y', 'j', 'P', '\x40',  '\x0', '\x87', '\xAE', '\x1B', '\x17', 'B', '\xB4', '\x3A',
  '\x3F', '\xBE', 'y', '\xC7', '\xA', '\x26', '\xB6', '\xEE',  '\xD9', '\x9A', '\x60', '\x14', '\x93', '\xDB', '\x8F', '\xD',
  '\xA', '\x2E', '\xE9', '\x23', '\x95', '\x29', 'X', '\x0',  '\x27', '\xEB', 'n', 'V', 'p', '\xBC', '\xD6', '\xCB',
  '\xD6', 'G', '\xAB', '\x3D', 'l', '\x7D', '\xB8', '\xD2',  '\xDD', '\xA0', '\x60', '\x83', '\xBA', '\xEF', '\x5F', '\xA4',
  '\xEA', '\xCC', '\x2', 'N', '\xAE', '\x5E', 'p', '\x1A',  '\xEC', '\xB3', '\x40', '9', '\xAC', '\xFE', '\xF2', '\x91',
  '\x89', 'g', '\x91', '\x85', '\x21', '\xA8', '\x87', '\xB7',  'X', '\x7E', '\x7E', '\x85', '\xBB', '\xCD', 'N', 'N',
  'b', 't', '\x40', '\xFA', '\x93', '\x89', '\xEC', '\x1E',  '\xEC', '\x86', '\x2', 'H', '\x26', '\x93', '\xD0', 'u',
  '\x1D', '\x7F', '\x9', '2', '\x95', '\xBF', '\x1F', '\xDB',  '\xD7', 'c', '\x8A', '\x1A', '\xF7', '\x5C', '\xC1', '\xFF',
  '\x22', 'J', '\xC3', '\x87', '\x0', '\x3', '\x0', 'K',  '\xBB', '\xF8', '\xD6', '\x2A', 'v', '\x98', 'I', '\x0',
  '\x0', '\x0', '\x0', 'I', 'E', 'N', 'D', '\xAE',  'B', '\x60', '\x82',
};

在网页中使用调试模式,打开连接 “localhost:8000/”,显示界面、两次请求如下。标签页的图标正常显示,而且长度在请求中也是555字节。
在这里插入图片描述

在这里插入图片描述
控制台结果实际为3次请求,控制台打印的解析请求头与web端截图完全一致。

20210812 13:46:00.293944Z  1467 WARN  HttpServer[dummy] starts listenning on 0.0.0.0:8000 - HttpServer.cc:53
20210812 13:46:25.036289Z  1467 INFO  TcpServer::newConnection [dummy] - new connection [dummy-0.0.0.0:8000#1] from 127.0.0.1:1071 - TcpServer.cc:80
20210812 13:46:25.036585Z  1467 INFO  TcpServer::newConnection [dummy] - new connection [dummy-0.0.0.0:8000#2] from 127.0.0.1:1072 - TcpServer.cc:80
Headers GET /
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
DNT: 1
Host: localhost:8000
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
20210812 13:46:25.301348Z  1467 INFO  TcpServer::newConnection [dummy] - new connection [dummy-0.0.0.0:8000#3] from 127.0.0.1:1073 - TcpServer.cc:80

关闭标签页,控制台无任何反应,关闭浏览器之后,3个连接断开

20210812 13:47:29.260650Z  1467 INFO  TcpServer::removeConnectionInLoop [dummy] - connection dummy-0.0.0.0:8000#2 - TcpServer.cc:109
20210812 13:47:29.260789Z  1467 INFO  TcpServer::removeConnectionInLoop [dummy] - connection dummy-0.0.0.0:8000#1 - TcpServer.cc:109
20210812 13:47:29.260821Z  1467 INFO  TcpServer::removeConnectionInLoop [dummy] - connection dummy-0.0.0.0:8000#3 - TcpServer.cc:109

标签:muduo,Http,--,0.0,resp,TcpServer,HttpServer,const,x0
来源: https://blog.csdn.net/wanggao_1990/article/details/119653235

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有