admin 管理员组文章数量: 887006
C++实现轻量级极简httpserver和httpclient(转)
转载自 .html 修改了部分编译问题
一般来说,C++的项目多是偏底层,不怎么需要跟http打交道,但有时候又需要在C++后端项目中加入一些简单 http接口,比如游戏运营服务器,金融交易监控服务等。
但是传统的实现方法比如采用libcurl,asio等较为重型的框架来做有没有必要,因此,这里采用mongoose这个库来实现基本的httpserver和httpclient功能,非常简单,包含一个h文件,一个cpp文件到工程中就行了,无需编译,无需链接库。
本文实现了一个project,将mongoose中提供的http相关api封装成了httpserver类和httpclient类,方便调用,目录结构如下:
├─common├─mongoose.h└─mongoose.cpp
├─httpclient├─http_client.h├─http_client.cpp└─main.cpp
└─httpserver└─web└─index.html├─http_server.h├─http_server.cpp└─main.cpp
编译环境:win10,vs2015, C++11 (其实是跨平台的)
http_server.h
#pragma once#include <string>
#include <unordered_map>
#include <functional>
#include "../common/mongoose.h"// 定义http返回callback
typedef void OnRspCallback(mg_connection *c, std::string);
// 定义http请求handler
using ReqHandler = std::function<bool (std::string, std::string, mg_connection *c, OnRspCallback)>;class HttpServer
{
public:HttpServer() {}~HttpServer() {}void Init(const std::string &port); // 初始化设置bool Start(); // 启动httpserverbool Close(); // 关闭void AddHandler(const std::string &url, ReqHandler req_handler); // 注册事件处理函数void RemoveHandler(const std::string &url); // 移除时间处理函数static std::string s_web_dir; // 网页根目录 static mg_serve_http_opts s_server_option; // web服务器选项static std::unordered_map<std::string, ReqHandler> s_handler_map; // 回调函数映射表private:// 静态事件响应函数static void OnHttpEvent(mg_connection *connection, int event_type, void *event_data);static void HandleEvent(mg_connection *connection, http_message *http_req);static void SendRsp(mg_connection *connection, std::string rsp);std::string m_port; // 端口mg_mgr m_mgr; // 连接管理器
};
http_server.cpp
#include <utility>
#include "http_server.h"void HttpServer::Init(const std::string &port)
{m_port = port;s_server_option.enable_directory_listing = "yes";s_server_option.document_root = s_web_dir.c_str();// TODO:其他设置
}bool HttpServer::Start()
{mg_mgr_init(&m_mgr, NULL);mg_connection *connection = mg_bind(&m_mgr, m_port.c_str(), OnHttpEvent);if (connection == NULL)return false;mg_set_protocol_http_websocket(connection);printf("starting http server at port: %s\n", m_port.c_str());// loopwhile (true)mg_mgr_poll(&m_mgr, 500); // msreturn true;
}void HttpServer::OnHttpEvent(mg_connection *connection, int event_type, void *event_data)
{http_message *http_req = (http_message *)event_data;switch (event_type){case MG_EV_HTTP_REQUEST:HandleEvent(connection, http_req);break;default:break;}
}static bool route_check(http_message *http_msg, char *route_prefix)
{if (mg_vcmp(&http_msg->uri, route_prefix) == 0)return true;elsereturn false;// TODO: 还可以判断 GET, POST, PUT, DELTE等方法//mg_vcmp(&http_msg->method, "GET");//mg_vcmp(&http_msg->method, "POST");//mg_vcmp(&http_msg->method, "PUT");//mg_vcmp(&http_msg->method, "DELETE");
}void HttpServer::AddHandler(const std::string &url, ReqHandler req_handler)
{if (s_handler_map.find(url) != s_handler_map.end())return;s_handler_map.insert(std::make_pair(url, req_handler));
}void HttpServer::RemoveHandler(const std::string &url)
{auto it = s_handler_map.find(url);if (it != s_handler_map.end())s_handler_map.erase(it);
}void HttpServer::SendRsp(mg_connection *connection, std::string rsp)
{// 必须先发送headermg_printf(connection, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");// 以json形式返回mg_printf_http_chunk(connection, "{ \"result\": %s }", rsp.c_str());// 发送空白字符快,结束当前响应mg_send_http_chunk(connection, "", 0);
}void HttpServer::HandleEvent(mg_connection *connection, http_message *http_req)
{std::string req_str = std::string(http_req->message.p, http_req->message.len);printf("got request: %s\n", req_str.c_str());// 先过滤是否已注册的函数回调std::string url = std::string(http_req->uri.p, http_req->uri.len);std::string body = std::string(http_req->body.p, http_req->body.len);auto it = s_handler_map.find(url);if (it != s_handler_map.end()){ReqHandler handle_func = it->second;handle_func(url, body, connection, SendRsp);}// 其他请求if (route_check(http_req, "/")) // index pagemg_serve_http(connection, http_req, s_server_option);else if (route_check(http_req, "/api/hello")) {// 直接回传SendRsp(connection, "welcome to httpserver");}else if (route_check(http_req, "/api/sum")){// 简单post请求,加法运算测试char n1[100], n2[100];double result;/* Get form variables */mg_get_http_var(&http_req->body, "n1", n1, sizeof(n1));mg_get_http_var(&http_req->body, "n2", n2, sizeof(n2));/* Compute the result and send it back as a JSON object */result = strtod(n1, NULL) + strtod(n2, NULL);SendRsp(connection, std::to_string(result));}else{mg_printf(connection,"%s","HTTP/1.1 501 Not Implemented\r\n""Content-Length: 0\r\n\r\n");}
}bool HttpServer::Close()
{mg_mgr_free(&m_mgr);return true;
}
index.html
<!DOCTYPE html>
<html>
<head><title>RESTful API demo</title><script src="//code.jquery/jquery-1.11.0.min.js"></script>
<script type="text/javascript">$(document).ready(function(){$("button").click(function(){$.get("/api/hello",function(data, status){console.log("get rsp: ", data);$('#result1').html(data);});});});$(document).on('keyup', '#n1, #n2', function() {$.ajax({url: '/api/sum',method: 'POST',dataType: 'json',data: { n1: $('#n1').val(), n2: $('#n2').val() },success: function(json) {console.log("post rsp: ", json);$('#result2').html(json.result);}});});</script>
</head>
<body><h1>c++ httpserver demo</h1><p>front end request</p><h2>GET</h2><div><button id="btn">get request</button></div><div><label>Result1:</label> <span id="result1"> </span></div><h2>POST</h2><div><label>Number 1:</label> <input type="text" id="n1" /></div><div><label>Number 2:</label> <input type="text" id="n2" /></div><div><label>Result2:</label> <span id="result2"> </span></div></body>
</html>
main.cpp
#include <iostream>
#include <memory>
#include "http_server.h"// 初始化HttpServer静态类成员
mg_serve_http_opts HttpServer::s_server_option;
std::string HttpServer::s_web_dir = "./web";
std::unordered_map<std::string, ReqHandler> HttpServer::s_handler_map;bool handle_fun1(std::string url, std::string body, mg_connection *c, OnRspCallback rsp_callback)
{// do sthstd::cout << "handle fun1" << std::endl;std::cout << "url: " << url << std::endl;std::cout << "body: " << body << std::endl;rsp_callback(c, "rsp1");return true;
}bool handle_fun2(std::string url, std::string body, mg_connection *c, OnRspCallback rsp_callback)
{// do sthstd::cout << "handle fun2" << std::endl;std::cout << "url: " << url << std::endl;std::cout << "body: " << body << std::endl;rsp_callback(c, "rsp2");return true;
}int main(int argc, char *argv[])
{std::string port = "7999";auto http_server = std::shared_ptr<HttpServer>(new HttpServer);http_server->Init(port);// add handlerhttp_server->AddHandler("/api/fun1", handle_fun1);http_server->AddHandler("/api/fun2", handle_fun2);http_server->RemoveHandler("/api/fun3");// http_server->RemoveHandler("/api/fun3");http_server->Start();return 0;
}
- 服务器支持host静态页面资源,也支持rest api调用
- 需要手动设置loop polling的时间间隔
- 可以自定义静态页面根路径,注册和解注册自定义api函数回调
- 某些变量必须声明定义成全局或者静态变量
http客户端
http_client.h
#pragma once
#include <string>
#include <functional>
#include "../common/mongoose.h"// 此处必须用function类,typedef再后面函数指针赋值无效
using ReqCallback = std::function<void (std::string)>;class HttpClient
{
public:HttpClient() {}~HttpClient() {}static void SendReq(const std::string &url, ReqCallback req_callback);static void OnHttpEvent(mg_connection *connection, int event_type, void *event_data);static int s_exit_flag;static ReqCallback s_req_callback;
};
http_client.cpp
#include "http_client.h"// 初始化client静态变量
int HttpClient::s_exit_flag = 0;
ReqCallback HttpClient::s_req_callback;// 客户端的网络请求响应
void HttpClient::OnHttpEvent(mg_connection *connection, int event_type, void *event_data)
{http_message *hm = (struct http_message *)event_data;int connect_status;switch (event_type) {case MG_EV_CONNECT:connect_status = *(int *)event_data;if (connect_status != 0) {printf("Error connecting to server, error code: %d\n", connect_status);s_exit_flag = 1;}break;case MG_EV_HTTP_REPLY:{printf("Got reply:\n%.*s\n", (int)hm->body.len, hm->body.p);std::string rsp = std::string(hm->body.p, hm->body.len);connection->flags |= MG_F_SEND_AND_CLOSE;s_exit_flag = 1; // 每次收到请求后关闭本次连接,重置标记// 回调处理s_req_callback(rsp);}break;case MG_EV_CLOSE:if (s_exit_flag == 0) {printf("Server closed connection\n");s_exit_flag = 1;};break;default:break;}
}// 发送一次请求,并回调处理,然后关闭本次连接
void HttpClient::SendReq(const std::string &url, ReqCallback req_callback)
{// 给回调函数赋值s_req_callback = req_callback;mg_mgr mgr;mg_mgr_init(&mgr, NULL);auto connection = mg_connect_http(&mgr, OnHttpEvent, url.c_str(), NULL, NULL);mg_set_protocol_http_websocket(connection);printf("Send http request %s\n", url.c_str());// loopwhile (s_exit_flag == 0)mg_mgr_poll(&mgr, 500);mg_mgr_free(&mgr);
}
- client每次请求都是一个独立的请求
- 请求函数中加入回调用于处理网络返回
测试
可以用浏览器、或者其他工具提交url,查看网络请求返回
GET
请求
http://localhost:7999/api/hello
结果
{ "result": welcome to httpserver }
POST
请求
http://localhost:7999/api/sum?n1=20&n2=18
结果
{ "result": 38 }
跨域问题参照:
本文标签: C实现轻量级极简httpserver和httpclient(转)
版权声明:本文标题:C++实现轻量级极简httpserver和httpclient(转) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1732354653h1534083.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论