C++技术之路:Http服务器

对于网络应用,Tcp,Http,websocket是常用的几个协议,今天我们来讲讲HttpServer方面的应用。我们直接上代码,来看看我平时的应用。

头文件

#pragma once

#include <string>
#include <string.h>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <utility>
#include <memory>
#include "mongoose.h"
#include <mutex>
#include <map>
// 定义http返回callback
typedef void OnRspCallback(mg_connection *c, std::string);
// 定义http请求handler
using ReqHandler = std::function<bool (unsigned int iSubHttpConID, std::string, std::string, mg_connection *c, OnRspCallback)>;

class GrockHttpServer
{
public:
	GrockHttpServer() {}
	~GrockHttpServer() {}

	void Init(const std::string &port); // 初始化设置
	bool Start(); // 启动httpserver
	bool 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; // 回调函数映射表

	void SendRspMsg(mg_connection *connection, std::string rsp);

private:
	// 静态事件响应函数
	static void OnHttpWebsocketEvent(mg_connection *connection, int event_type, void *event_data);

	static void HandleHttpEvent(mg_connection *connection, http_message *http_req);
	static void SendHttpRsp(mg_connection *connection, std::string rsp);

	static int isWebsocket(const mg_connection *connection); // 判断是否是websoket类型连接
	static void HandleWebsocketMessage(mg_connection *connection, int event_type, websocket_message *ws_msg); 
	static void SendWebsocketMsg(mg_connection *connection, std::string msg); // 发送消息到指定连接
	static void BroadcastWebsocketMsg(std::string msg); // 给所有连接广播消息
	static std::unordered_set<mg_connection *> s_websocket_session_set; // 缓存websocket连接

	std::string m_port;    // 端口
	mg_mgr m_mgr;          // 连接管理器
};

class CGrockHttpSsMg
{
public:
	CGrockHttpSsMg() { m_HttpServer = std::shared_ptr<GrockHttpServer>(new GrockHttpServer); }
	static CGrockHttpSsMg& Single() { static CGrockHttpSsMg single; return single; }

	void Start(const std::string& port);

	unsigned CreateHttpConID(mg_connection* conn);
	void SendRspMsg(unsigned int iSubConID, std::string rsp);

	void AddHandler(const std::string &url, ReqHandler req_handler)
	{
		m_HttpServer->AddHandler(url, req_handler);
	}

private:
	~CGrockHttpSsMg() {}

private:
	std::mutex m_mutex;
	std::map<unsigned, mg_connection*>	m_HttpMg;

	std::shared_ptr<GrockHttpServer> m_HttpServer;
};

这里我用到了mongoose.h
mongoose的代码着实轻量,先看看它的特点:

  1. 在整个的实现是使用C语言编写

  2. 整个代码也只有一个mongoose.c和mongoose.h两个文件, 从引入第三方的考虑上也着实不多。

  3. 实现的功能还是非常多的,从使用的层面上来说功能还是比较全面。只不过不知道是否是为了第三方使用的方便还是怎么地,它的代码只用了两个源文件罢了。诸多的功能也大以宏的开始与结束来区分。

源文件:

#include "./GrockHttpServer.h"
#include <thread>
 
mg_serve_http_opts GrockHttpServer::s_server_option;
std::string GrockHttpServer::s_web_dir = "./web";
std::unordered_map<std::string, ReqHandler> GrockHttpServer::s_handler_map;
std::unordered_set<mg_connection *> GrockHttpServer::s_websocket_session_set;


void GrockHttpServer::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();
}

bool GrockHttpServer::Start()
{
	mg_mgr_init(&m_mgr, NULL);
	mg_connection *connection = mg_bind(&m_mgr, m_port.c_str(), GrockHttpServer::OnHttpWebsocketEvent);
	if (connection == NULL)
		return false;
	mg_set_protocol_http_websocket(connection);

	printf("starting http server at port: %s\n", m_port.c_str());
	while (true)
		mg_mgr_poll(&m_mgr, 500);
 
	return true;
}

void GrockHttpServer::OnHttpWebsocketEvent(mg_connection *connection, int event_type, void *event_data)
{
	if (event_type == MG_EV_HTTP_REQUEST)
	{
		http_message *http_req = (http_message *)event_data;
		HandleHttpEvent(connection, http_req);
	}
	else if (event_type == MG_EV_WEBSOCKET_HANDSHAKE_DONE ||
		     event_type == MG_EV_WEBSOCKET_FRAME ||
		     event_type == MG_EV_CLOSE)
	{
		websocket_message *ws_message = (struct websocket_message *)event_data;
		HandleWebsocketMessage(connection, event_type, ws_message);
	}
}

static bool route_check(http_message *http_msg, char *route_prefix)
{
	if (mg_vcmp(&http_msg->uri, route_prefix) == 0)
	{
		return true;
	}
	else
		return false;
}

void GrockHttpServer::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 GrockHttpServer::RemoveHandler(const std::string &url)
{
	auto it = s_handler_map.find(url);
	if (it != s_handler_map.end())
		s_handler_map.erase(it);
}

void GrockHttpServer::SendHttpRsp(mg_connection *connection, std::string rsp)
{
	mg_printf(connection, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
	mg_printf_http_chunk(connection, "%s", rsp.c_str());
	mg_send_http_chunk(connection, "", 0);
}

void GrockHttpServer::SendRspMsg(mg_connection *connection, std::string rsp)
{
	mg_printf(connection, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
	mg_printf_http_chunk(connection, "%s", rsp.c_str());
	mg_send_http_chunk(connection, "", 0);
}

void GrockHttpServer::HandleHttpEvent(mg_connection *connection, http_message *http_req)
{
	unsigned iSubConID = CGrockHttpSsMg::Single().CreateHttpConID(connection);
	if (iSubConID == 0)
		return;

	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(iSubConID, url, body, connection, &GrockHttpServer::SendHttpRsp);
	}
	else
	{
		if (route_check(http_req, "/api/hello"))
		{
			SendHttpRsp(connection, "welcome to GrockHttpServer");
		}
		else
		{
			mg_printf(
				connection,
				"%s",
				"HTTP/1.1 501 Not Implemented\r\n"
				"Content-Length: 0\r\n\r\n");
		}
	}
}

// ---- websocket ---- //
int GrockHttpServer::isWebsocket(const mg_connection *connection)
{
	return connection->flags & MG_F_IS_WEBSOCKET;
}

void GrockHttpServer::HandleWebsocketMessage(mg_connection *connection, int event_type, websocket_message *ws_msg)
{
	if (event_type == MG_EV_WEBSOCKET_HANDSHAKE_DONE)
	{
		printf("client websocket connected\n");
		char addr[32];
		mg_sock_addr_to_str(&connection->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
		printf("client addr: %s\n", addr);
		s_websocket_session_set.insert(connection);
		SendWebsocketMsg(connection, "client websocket connected");
	}
	else if (event_type == MG_EV_WEBSOCKET_FRAME)
	{
		mg_str received_msg = {
			(char *)ws_msg->data, ws_msg->size
		};

		char buff[1024] = {0};
		strncpy(buff, received_msg.p, received_msg.len); // must use strncpy, specifiy memory pointer and length
		printf("received msg: %s\n", buff);
		SendWebsocketMsg(connection, "send your msg back: " + std::string(buff));
	}
	else if (event_type == MG_EV_CLOSE)
	{
		if (isWebsocket(connection))
		{
			printf("client websocket closed\n");
			if (s_websocket_session_set.find(connection) != s_websocket_session_set.end())
				s_websocket_session_set.erase(connection);
		}
	}
}

void GrockHttpServer::SendWebsocketMsg(mg_connection *connection, std::string msg)
{
	mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, msg.c_str(), strlen(msg.c_str()));
}

void GrockHttpServer::BroadcastWebsocketMsg(std::string msg)
{
	for (mg_connection *connection : s_websocket_session_set)
		mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, msg.c_str(), strlen(msg.c_str()));
}

bool GrockHttpServer::Close()
{
	mg_mgr_free(&m_mgr);
	return true;
}

///////////////////////////////////////////////////////////////////
void CGrockHttpSsMg::Start(const std::string& port)
{
	auto fun = [](std::shared_ptr<GrockHttpServer> pHttpServer, 
		const std::string& strPort) {
		pHttpServer->Init(strPort);
		pHttpServer->Start();
	};

	std::thread t(fun, m_HttpServer, port);
	t.detach();
}

unsigned CGrockHttpSsMg::CreateHttpConID(mg_connection* conn)
{
	static unsigned int iHttpConID = 20000;
	unsigned int iTempID = iHttpConID;

	m_mutex.lock();
	typename std::map<unsigned, mg_connection*>::iterator iter = m_HttpMg.find(iTempID);

	if (iter == m_HttpMg.end())
	{
		m_HttpMg.insert(std::map<unsigned, mg_connection*>::value_type(iTempID, conn));
		m_mutex.unlock();

		iHttpConID += 1;
		return iTempID;
	}
	m_mutex.unlock();
	return 0;
}

void CGrockHttpSsMg::SendRspMsg(unsigned int iSubConID, std::string rsp)
{
	m_mutex.lock();
	typename std::map<unsigned, mg_connection*>::iterator iter = m_HttpMg.find(iSubConID);

	if (iter != m_HttpMg.end())
	{
		mg_connection* connection = iter->second;
		m_HttpMg.erase(iter);
		m_mutex.unlock();

		m_HttpServer->SendRspMsg(connection, rsp);
	}
	else
	{
		m_mutex.unlock();
		printf("不存在这个Http连接,应该已经回应\n");
	}
}

发布了11 篇原创文章 · 获赞 0 · 访问量 299

猜你喜欢

转载自blog.csdn.net/slyw77slyw/article/details/105419710