基于libevent的C++高并发、易扩展HTTP服务迭代之路

这篇具有很好参考价值的文章主要介绍了基于libevent的C++高并发、易扩展HTTP服务迭代之路。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一 前言

在项目或产品开发中,需要用C++实现一个高并发且易扩展的HTTP Server,那么我们可以基于libevent来做这件事情。Libevent提供了HTTP Server等组件,并且支持多线程编程。下面我们一起来看一下实现过程。

二 初版代码

如何用libevent实现一个自己的HTTP Server呢?网上有很多的文章和资料,可供参考。最简单的一种方式就是在main函数中直接调用其接口,实现服务端程序,这种方法的优点是简便易上手。缺点是HTTP服务请求处理过程,直接在程序主线程中,会卡住主线程,所以在主线程中没法进行其它业务操作。

下边启动一个HTTP Server工作线程,在后台处理HTTP请求,也就解决了上边卡主线程的问题。Libevent使用从官网下载的最新版libevent-2.1.12-stable,开发环境:Win10系统,VS2022。我们设计了一个CMyHTTPServer类,封装了libevent的相关接口,类的属性、方法及其它声明定义请参见下边代码,看代码之前,先了解下server主要流程。

代码流程

1 创建HTTP服务后台工作线程

使用std::thread

2 创建event base对象

EVENT2_EXPORT_SYMBOL
struct event_base *event_base_new(void);

3 创建http server

EVENT2_EXPORT_SYMBOL

struct evhttp *evhttp_new(struct event_base *base);

4 设置http请求回调函数

EVENT2_EXPORT_SYMBOL
void evhttp_set_gencb(struct evhttp *http, void (*cb)(struct evhttp_request *, void *), void *arg);

5 绑定、监听IP和端口

EVENT2_EXPORT_SYMBOL
struct evhttp_bound_socket *evhttp_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port);

6 进入事件循环

EVENT2_EXPORT_SYMBOL
int event_base_dispatch(struct event_base *);

7 在回调函数中,处理客户端各种HTTP请求

源代码

代码中有详细的注释,就不多说了,请看代码。

MyDefine.h

#pragma once

#define HTTP_SERVER_LISTEN_IP		"0.0.0.0"							//http服务监听地址
#define HTTP_SERVER_LISTEN_PORT		8080								//http服务监听端口

#define HTTP_CLIENT_LOGIN			"/client?Action=Login"				//系统登录URI
#define HTTP_CLIENT_LOGOUT			"/client?Action=Logout"				//系统登出URI
#define HTTP_CLIENT_HEARBEAT		"/client?Action=Heartbeat"			//心跳URI

/*
*  系统各种业务请求URL宏定义,格式与登录、登出、心跳类似
*/

MyHeader.h

#pragma once

#include<stdio.h>
#include<mutex>
#include<thread>
#include<string>
#include<map>
#include<vector>
#include<iostream>

#include"event2/bufferevent.h"
#include"event2/buffer.h"
#include"event2/listener.h"
#include"event2/util.h"
#include"event2/event_compat.h"
#include"event2/event.h"
#include"event2/keyvalq_struct.h"
#include"event2/http.h"
#include"event2/http_struct.h"
#include"event2/http_compat.h"

using std::mutex;
using std::thread;
using std::string;
using std::map;
using std::vector;

CMyHttpServer.h

#pragma once
#include"MyHeader.h"

/*****************************************************************************
**FileName: MyHeader.h
**Function: http服务器启动/停止,接收客户端http请求及处理
**Version record:
**Version       Author              Data                Description
**v1.0.0        chexlong			2022.09             初稿
*****************************************************************************/

class CMyHttpServer
{
public:

	CMyHttpServer(const int& listenPort);
	~CMyHttpServer();

	//启动http服务
	int Start();
	//停止http服务
	int Stop();  

private:
	//处理文件请求
	void OnRequestFile(struct evhttp_request* pstReq);

	//处理数据请求
	void OnRequestData(struct evhttp_request* pstReq);

	//处理系统各种业务的GET请求
	void RequestProcessGet(struct evhttp_request* pstReq);

	//处理系统各种业务的POST请求
	void RequestProcessPost(struct evhttp_request* pstReq);

	//http请求回调函数
	static void HttpReqCallback(struct evhttp_request* pstReq, void* userData);

	//http工作线程函数
	void WorkThread();

	//发送http请求应答消息
	int SendReply(struct evhttp_request* pstReq, int code, const char* reason, struct evbuffer* evb);

private:

	//event base
	event_base*				m_base;

	//http server
	evhttp*					m_http;

	//绑定监听socket句柄
	evhttp_bound_socket*	m_handle;

	//http服务线程
	std::thread				m_thread;

	//http监听端口
	int						m_listenPort;
};

CMyHttpServer.cpp

#include"CMyHttpServer.h"
#include"MyDefine.h"

CMyHttpServer::CMyHttpServer(const int& listenPort)
{
    m_base = nullptr;
    m_http = nullptr;
    m_handle = nullptr;
    m_listenPort = listenPort;
}

CMyHttpServer::~CMyHttpServer()
{
    Stop();
}

int CMyHttpServer::SendReply(struct evhttp_request* pstReq, int code, const char* reason, struct evbuffer* evb)
{
    if (nullptr == pstReq)
    {
        if (evb)
        {
            evbuffer_free(evb);
        }
        return -1;
    }

    //返回HTTP头部
    evhttp_add_header(pstReq->output_headers, "Server", "MyHttpServer");
    evhttp_add_header(pstReq->output_headers, "Content-Type", "application/json");
    evhttp_add_header(pstReq->output_headers, "Connection", "keep-alive");

    //发送应答
    evhttp_send_reply(pstReq, code, reason, evb);
    if (evb)
    {
        evbuffer_free(evb);
    }
    return 0;
}

int CMyHttpServer::Start()
{
    m_thread = std::move(std::thread([this]() {
        WorkThread();
        }));
    m_thread.detach();

    return 0;
}

int CMyHttpServer::Stop()
{
    if (m_base)
    {
        event_base_loopbreak(m_base);
        event_base_free(m_base);
        m_base = nullptr;
    }
    return 0;
}

void CMyHttpServer::WorkThread()
{
    //创建event base对象
    m_base = event_base_new();
    if (!m_base)
    {
        std::cout << "create event base failed." << std::endl;
        return;
    }
    //创建http server
    m_http = evhttp_new(m_base);
    if (!m_http)
    {
        std::cout << "create evhttp failed." << std::endl;
        goto err;
    }
    //设置http请求回调函数
    evhttp_set_gencb(m_http, HttpReqCallback, this);

    //绑定、监听IP和端口
    m_handle = evhttp_bind_socket_with_handle(m_http, HTTP_SERVER_LISTEN_IP, m_listenPort);
    if (!m_handle)
    {
        std::cout << "bind socket failed, please check port has been used." << std::endl;
        goto err;
    }

    std::cout << "http server started." << std::endl;
    //进入事件循环
    event_base_dispatch(m_base);

err:

    //停止接收新的客户端连接
    if(m_handle)
        evhttp_del_accept_socket(m_http, m_handle);

    //销毁和释放http server资源
    if(m_http)
        evhttp_free(m_http);

    //销毁和释放event base资源
    if(m_base)
        event_base_free(m_base);
}

void CMyHttpServer::HttpReqCallback(struct evhttp_request* pstReq, void* userData)
{
    evhttp_cmd_type cmdType = evhttp_request_get_command(pstReq);
    if (EVHTTP_REQ_GET == cmdType || EVHTTP_REQ_POST == cmdType)
    {
        CMyHttpServer* this_ = (CMyHttpServer*)userData;
        if (!this_)
        {
            std::cout << "get this failed." << std::endl;
            evhttp_send_error(pstReq, HTTP_BADREQUEST, "Bad Request");
            return;
        }
        //URI中包含?的,用于数据请求;否则用于文件请求
        const char* uri = evhttp_request_get_uri(pstReq);
        if (strstr(uri, "?"))
            this_->OnRequestData(pstReq);
        else
            this_->OnRequestFile(pstReq);
    }
    else
    {
        std::cout << "not support request." << std::endl;
        evhttp_send_error(pstReq, HTTP_BADREQUEST, "Bad Request");
    }
}

void CMyHttpServer::OnRequestFile(evhttp_request* pstReq)
{
    //TODO:文件下载逻辑代码
}

void CMyHttpServer::OnRequestData(struct evhttp_request* pstReq)
{
    if (nullptr == pstReq)
    {
        std::cout << "invalid parameter." << std::endl;
        return;
    }

    evhttp_cmd_type cmdType = evhttp_request_get_command(pstReq);
    if (EVHTTP_REQ_GET == cmdType) //GET请求
    {
        RequestProcessGet(pstReq);
    }
    else if (EVHTTP_REQ_POST == cmdType) //POST请求
    {
        RequestProcessPost(pstReq);
    }
    else
    {
        std::cout << "not support method." << std::endl;
        SendReply(pstReq, HTTP_BADMETHOD, "NOT-SUPPORT-METHOD", NULL);
    }
}

void CMyHttpServer::RequestProcessGet(evhttp_request* pstReq)
{
    //TODO:系统各种业务的GET请求
}

void CMyHttpServer::RequestProcessPost(evhttp_request* pstReq)
{
    evhttp_cmd_type cmdType = EVHTTP_REQ_POST;
    const char* puri = evhttp_request_get_uri(pstReq);
    std::string suri(puri);
    char bodyBuf[1024] = { 0 };
    if (pstReq->body_size > 0)
    {
        evbuffer_remove(pstReq->input_buffer, bodyBuf, pstReq->body_size);
        std::cout << "POST request uri:" << suri << std::endl << "msg body:" << bodyBuf << std::endl;
    }
    else
    {
        std::cout << "POST request uri:" << suri << std::endl;
    }

    if (suri == std::string(HTTP_CLIENT_LOGIN))
    {
        //TODO:登录
    }
    else if (suri == std::string(HTTP_CLIENT_LOGOUT))
    {
        //TODO:登出
    }
    else if (suri == std::string(HTTP_CLIENT_HEARBEAT))
    {
        //TODO:心跳
    }
    /*else if (suri == std::string(HTTP_CLIENT_XXX))
    {
        //TODO:系统各种业务的POST请求
    }*/
    else
    {
        std::cout << "not support get method" << std::endl;
        SendReply(pstReq, HTTP_BADMETHOD, "NOT-SUPPORT-GET", NULL);
    }
    SendReply(pstReq, HTTP_OK, "200 OK", NULL);
}

CMyHttpServer.cpp

int main()
{
#ifdef WIN32
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif

    std::cout << "Hello MyHttpServer!\n";

    CMyHttpServer myHttpServer(HTTP_SERVER_LISTEN_PORT);

    myHttpServer.Start();
	while (true)
	{
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
	myHttpServer.Stop();
	std::cout << "Stop MyHttpServer!\n";
	system("pause");

#ifdef WIN32
	WSACleanup();
#endif
}

下边是VS2022中的项目工程截图,有个直观的总体认识。

基于libevent的C++高并发、易扩展HTTP服务迭代之路

工程配置

头文件包含目录

基于libevent的C++高并发、易扩展HTTP服务迭代之路

Lib库目录

基于libevent的C++高并发、易扩展HTTP服务迭代之路

引入Lib文件

基于libevent的C++高并发、易扩展HTTP服务迭代之路

运行调测

编译、启动运行,使用postman发送登录和登出模拟请求。

登录请求

基于libevent的C++高并发、易扩展HTTP服务迭代之路

登出请求

基于libevent的C++高并发、易扩展HTTP服务迭代之路

控制台打印输出

基于libevent的C++高并发、易扩展HTTP服务迭代之路

单线程模式的思考

对于并发量要求不高、业务请求种类不多的场景,单线程的HTTP服务完全能满足要求了。但是单线程模式,还存在哪些缺陷呢?

存在问题:

1 虽然启用了HTTP Server后台工作线程,但只有1个。如果有多个客户端同时发送请求,或者Server端某个请求操作比较耗时,可能阻塞当前线程。显而易见,面对这种用户场景,单线程的HTTP Server,其并发量还是不够的。

2 在CMyHttpServer::RequestProcessPost函数中,目前有3个if/else分支,随着项目迭代及业务变更,可能会有几十甚至上百的if/else分支,该怎么优化,以便与扩展和维护呢?

三 第二版代码

解决思路

针对第1个问题,查看libevent资料后得知,libevent多线程编程的关键是将每个事件关联到自己的event_base。回过头来再看一下CMyHttpServer类,已经封装了event_base,且类的一个对象实例,可以启用一个HTTP Server后台工作线程。依据这一思路,我们可以再创建一个管理类CMyHttpServerMgr,在这个类中,创建多个CMyHttpServer实例对象,每个实例启用一个工作线程。

针对第2个问题,我们详细看一下HTTP URI的定义格式,有没有什么规律:

基于libevent的C++高并发、易扩展HTTP服务迭代之路

红线框中的路径都是一样的,就后边的方法名称不一样。把方法名称当做关键字Key,来定义一组HTTP请求处理函数映射列表。具体实现技术用到了函数指针,达到接口与实现的解耦。

根据上边的优化思路,第二版代码来了。

代码流程

1 创建HTTP Server管理类的套接字监听线程

std::thread

2 创建监听套接字,并开启监听

BindSocket

3 创建HTTP Server后台工作线程池

4 初始化HTTP请求消息映射表

5 启动HTTP Server后台工作线程

std::thread

6 创建event base对象

event_base_new

7 创建http server

evhttp_new

8 接收新的连接请求

evhttp_accept_socket

9 设置http请求回调函数

evhttp_set_gencb

10 进入事件循环

event_base_dispatch

11在回调函数中,并发处理客户端各种HTTP请求

源代码

MyHeader.h和MyDefine.h两个文件没变动。

MyHttpCmdDef.h(新加)

#pragma once
#include"MyHeader.h"

//HTTP请求消息映射结构体
struct HTTPReqInfo
{
	//HTTP消息处理关键字
	const char* cmdKey;

	//HTTP消息处理函数地址
	int(*called_fun)(const struct evhttp_request* pstReq, const string& data, void* userData);
};

struct HTTPReqInfoMap
{
	//请求消息索引
	int index;

	//HTTP请求命令
	struct HTTPReqInfo* cmd;
};

//存储HTTP命令请求的map表
typedef map<string, HTTPReqInfoMap> HTTP_REQ_INFO_MAP;

CMyHttpServer.h(更新)

#pragma once
#include"MyHeader.h"
#include"MyHttpCmdDef.h"

/*****************************************************************************
**FileName: MyHeader.h
**Function: http服务器启动/停止,接收客户端http请求及处理
**Version record:
**Version       Author        Data            Description
**v1.0.0        chexlong      2022.09         初稿
**v1.0.2        chexlong	  2022.09         1,支持多线程 
**											  2,添加HTTP请求命令模式
*****************************************************************************/

class CMyHttpServer;
typedef std::shared_ptr<CMyHttpServer>		CMyHttpServerPtr;
typedef vector<CMyHttpServerPtr>			MyHTTPServerVec;

class CMyHttpServerMgr
{
public:
	CMyHttpServerMgr(const int& listenPort);
	~CMyHttpServerMgr();

	//启动http服务
	int Start();

	//停止http服务
	int Stop();

private:
	//监听线程
	void ListenThreadFunc();

	//创建套接字,绑定地址和端口,开启监听
	int BindSocket(int port, int backlog);
private:

	//http服务监听线程
	std::thread				m_thread;

	//http server监听端口
	int						m_listenPort;
	//监听套接字
	int						m_listenSocket;

	//http消息处理线程池
	MyHTTPServerVec			m_httpServerPool;
};

class CMyHttpServer
{
public:

	CMyHttpServer(const int& listenSocket);
	~CMyHttpServer();

	//启动http服务
	int Start();
	//停止http服务
	int Stop();  

private:
	//处理文件请求
	void OnRequestFile(struct evhttp_request* pstReq);

	//处理数据请求
	void OnRequestData(struct evhttp_request* pstReq);

	//处理系统各种业务的GET请求
	void RequestProcessGet(struct evhttp_request* pstReq);

	//处理系统各种业务的POST请求
	void RequestProcessPost(struct evhttp_request* pstReq);

	//http请求回调函数
	static void HttpReqCallback(struct evhttp_request* pstReq, void* userData);

	//http工作线程函数
	void WorkThread();

	//获取http请求负载数据
	std::string GetContentFromRequest(struct evhttp_request* req);

	//发送http请求应答消息
	int SendReply(struct evhttp_request* pstReq, int code, const char* reason, struct evbuffer* evb);

private:
	static int Login(const struct evhttp_request* pstReq, const string& data, void* param);
	static int Logout(const struct evhttp_request* pstReq, const string& data, void* param);
	static int Heartbeat(const struct evhttp_request* pstReq, const string& data, void* param);

private:

	//event base
	event_base*				m_base;

	//http server
	evhttp*					m_http;

	//绑定监听socket句柄
	//evhttp_bound_socket*	m_handle;

	//http服务工作线程
	std::thread				m_thread;

	//http监听套接字
	int						m_listenSocket;

private:

	//HTTP请求消息映射列表
	struct HTTPReqInfo httpReqInfo[10] =
	{
		{"Login",		CMyHttpServer::Login,},
		{"Logout",		CMyHttpServer::Logout,},
		{"Heartbeat",	CMyHttpServer::Heartbeat,},
		{ NULL }
	};

	HTTP_REQ_INFO_MAP	m_httpReqMap;
};

CMyHttpServer.cpp(更新)

#include"CMyHttpServer.h"
#include"MyDefine.h"

CMyHttpServerMgr::CMyHttpServerMgr(const int& listenPort)
{
    m_listenPort = listenPort;
    m_listenSocket = INVALID_SOCKET;
}
CMyHttpServerMgr::~CMyHttpServerMgr()
{
}

int CMyHttpServerMgr::Start()
{
    m_thread = std::move(std::thread([this]() {
        ListenThreadFunc();
        }));
    m_thread.detach();

    return 0;
}

int CMyHttpServerMgr::Stop()
{
    for (auto& httpServer : m_httpServerPool)
    {
        httpServer->Stop();
    }
    return 0;
}

void CMyHttpServerMgr::ListenThreadFunc()
{
    std::cout << "http server listen thread id : " << std::this_thread::get_id() << std::endl;

    //创建监听套接字,并开启监听
    int result = BindSocket(m_listenPort, SOMAXCONN);
    if (0 != result)
    {
        std::cout << "HTTP服务监听套接字创建失败,端口:" << m_listenPort << std::endl;
        return;
    }
    std::cout << "HTTP服务监听端口:" << m_listenPort << std::endl;

    //线程池数量:CPU核数 x 2
    int threadPoolSize = std::thread::hardware_concurrency() * 2;
    for (int i = 0; i < threadPoolSize; i++)
    {
        CMyHttpServerPtr httpServer(new CMyHttpServer(m_listenSocket));
        httpServer->Start();
        m_httpServerPool.push_back(httpServer);
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

int CMyHttpServerMgr::BindSocket(int port, int backlog)
{
    //创建监听套接字
    m_listenSocket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_listenSocket == INVALID_SOCKET)
    {
        std::cout << "create listen socket failed." << std::endl;
        return -1;
    }

    //地址可复用
    int result = 0, optval = 1;
    result = setsockopt(m_listenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof(int));

    //设为非阻塞模式
    int block = 1;
    result = ::ioctlsocket(m_listenSocket, FIONBIO, (u_long FAR*) & block);
    if (SOCKET_ERROR == result)
    {
        std::cout << "ioctlsocket failed : " << WSAGetLastError() << std::endl;
        closesocket(m_listenSocket);
        m_listenSocket = INVALID_SOCKET;
        return -1;
    }

    struct sockaddr_in local_addr;
    memset(&local_addr, 0, sizeof(struct sockaddr_in));
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(port);
    local_addr.sin_addr.s_addr = INADDR_ANY;

    //绑定IP地址和端口
    if (INVALID_SOCKET == ::bind(m_listenSocket, (struct sockaddr*)&local_addr, sizeof(struct sockaddr)))
    {
        std::cout << "bind failed : " << WSAGetLastError() << std::endl;
        closesocket(m_listenSocket);
        m_listenSocket = INVALID_SOCKET;
        return -1;
    }

    //开启监听
    result = listen(m_listenSocket, backlog);
    if (result < 0)
    {
        std::cout << "listen failed : " << WSAGetLastError() << std::endl;
        closesocket(m_listenSocket);
        m_listenSocket = INVALID_SOCKET;
        return -1;
    }
    return 0;
}

CMyHttpServer::CMyHttpServer(const int& listenSocket)
{
    m_base = nullptr;
    m_http = nullptr;
    //m_handle = nullptr;
    m_listenSocket = listenSocket;
}

CMyHttpServer::~CMyHttpServer()
{
    Stop();
}

int CMyHttpServer::SendReply(struct evhttp_request* pstReq, int code, const char* reason, struct evbuffer* evb)
{
    if (nullptr == pstReq)
    {
        if (evb)
        {
            evbuffer_free(evb);
        }
        return -1;
    }

    //返回HTTP头部
    evhttp_add_header(pstReq->output_headers, "Server", "MyHttpServer");
    evhttp_add_header(pstReq->output_headers, "Content-Type", "application/json");
    evhttp_add_header(pstReq->output_headers, "Connection", "keep-alive");

    //发送应答
    evhttp_send_reply(pstReq, code, reason, evb);
    if (evb)
    {
        evbuffer_free(evb);
    }
    return 0;
}

int CMyHttpServer::Start()
{
    //初始化HTTP请求消息映射表
    struct HTTPReqInfo* cmd = httpReqInfo;
    int index = 0;
    for (; cmd->cmdKey != NULL; cmd++)
    {
        struct HTTPReqInfoMap cmdMap;
        cmdMap.index = index;
        cmdMap.cmd = cmd;
        m_httpReqMap[cmd->cmdKey] = cmdMap;
        index++;
    }

   //启动http服务工作线程
    m_thread = std::move(std::thread([this]() {
        WorkThread();
        }));
    m_thread.detach();

    return 0;
}

int CMyHttpServer::Stop()
{
    if (m_base)
    {
        event_base_loopbreak(m_base);
        event_base_free(m_base);
        m_base = nullptr;
    }
    return 0;
}

void CMyHttpServer::WorkThread()
{
    std::cout << "http server work thread id : " << std::this_thread::get_id() << std::endl;

    //创建event base对象
    m_base = event_base_new();
    if (!m_base)
    {
        std::cout << "create event base failed." << std::endl;
        return;
    }
    //创建http server
    m_http = evhttp_new(m_base);
    if (!m_http)
    {
        std::cout << "create evhttp failed." << std::endl;
        goto err;
    }

    //接收新的连接请求
    if (0 != evhttp_accept_socket(m_http, m_listenSocket))
    {
        std::cout << "evhttp accecpt failed." << std::endl;
        goto err;
    }

    //设置HTTP请求超时处理时间,60秒
    evhttp_set_timeout(m_http, 60);

    //设置HTTP支持的请求类型
    evhttp_set_allowed_methods(m_http, EVHTTP_REQ_GET | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_POST);

    //设置http请求回调函数
    evhttp_set_gencb(m_http, HttpReqCallback, this);

    std::cout << "http server started." << std::endl;
    //进入事件循环
    event_base_dispatch(m_base);

err:
    //销毁和释放http server资源
    if(m_http)
        evhttp_free(m_http);

    //销毁和释放event base资源
    if(m_base)
        event_base_free(m_base);
}

void CMyHttpServer::HttpReqCallback(struct evhttp_request* pstReq, void* userData)
{
    std::cout << "HttpReqCallback thread id : " << std::this_thread::get_id() << std::endl;

    evhttp_cmd_type cmdType = evhttp_request_get_command(pstReq);
    if (EVHTTP_REQ_GET == cmdType || EVHTTP_REQ_POST == cmdType)
    {
        CMyHttpServer* this_ = (CMyHttpServer*)userData;
        if (!this_)
        {
            std::cout << "get this failed." << std::endl;
            evhttp_send_error(pstReq, HTTP_BADREQUEST, "Bad Request");
            return;
        }
        //URI中包含?的,用于数据请求;否则用于文件请求
        const char* uri = evhttp_request_get_uri(pstReq);
        if (strstr(uri, "?"))
            this_->OnRequestData(pstReq);
        else
            this_->OnRequestFile(pstReq);
    }
    else
    {
        std::cout << "not support request." << std::endl;
        evhttp_send_error(pstReq, HTTP_BADREQUEST, "Bad Request");
    }
}

void CMyHttpServer::OnRequestFile(evhttp_request* pstReq)
{
    //TODO:文件下载逻辑代码
}

void CMyHttpServer::OnRequestData(struct evhttp_request* pstReq)
{
    if (nullptr == pstReq)
    {
        std::cout << "invalid parameter." << std::endl;
        return;
    }

    evhttp_cmd_type cmdType = evhttp_request_get_command(pstReq);
    if (EVHTTP_REQ_GET == cmdType) //GET请求
    {
        RequestProcessGet(pstReq);
    }
    else if (EVHTTP_REQ_POST == cmdType) //POST请求
    {
        RequestProcessPost(pstReq);
    }
    else
    {
        std::cout << "not support method." << std::endl;
        SendReply(pstReq, HTTP_BADMETHOD, "NOT-SUPPORT-METHOD", NULL);
    }
}

void CMyHttpServer::RequestProcessGet(evhttp_request* pstReq)
{
    //TODO:系统各种业务的GET请求
}

std::string CMyHttpServer::GetContentFromRequest(struct evhttp_request* req)
{
    std::string data;
    struct evbuffer* buf = evhttp_request_get_input_buffer(req);
    while (evbuffer_get_length(buf))
    {
        int n;
        char cbuf[256];
        memset(cbuf, 0, sizeof(cbuf));
        n = evbuffer_remove(buf, cbuf, sizeof(cbuf));
        if (n > 0)
        {
            data.append(cbuf, n);
        }
    }
    return data;
}

void CMyHttpServer::RequestProcessPost(evhttp_request* pstReq)
{
    //获取请求URI
    evhttp_cmd_type cmdType = EVHTTP_REQ_POST;
    const char* puri = evhttp_request_get_uri(pstReq);

    struct evkeyvalq headers;
    if (evhttp_parse_query(puri, &headers) != 0)
    {
        std::cout << "http bad request." << std::endl;
        evhttp_send_error(pstReq, HTTP_BADREQUEST, 0);
        return;
    }
    //获取请求方法
    const char* cmd = evhttp_find_header(&headers, "Action");
    if (cmd == NULL)
    {
        std::cout << "http bad request." << std::endl;
        evhttp_send_error(pstReq, HTTP_BADREQUEST, 0);
        return;
    }
    //获取http请求负载数据
    std::string jsonData(std::move(GetContentFromRequest(pstReq)));

    //http请求消息分发
    if (m_httpReqMap.count(cmd) > 0)
    {
        struct HTTPReqInfo* cmdFound = m_httpReqMap.at(cmd).cmd;
        if (cmdFound && cmdFound->called_fun)
        {
            std::string suri(puri);
            std::cout << "POST request uri:" << suri << std::endl << "msg body:" << jsonData << std::endl;

            //触发回调
            cmdFound->called_fun(pstReq, jsonData, this);
        }
        else
        {
            std::cout << "invalid http request cmd : " << cmd << std::endl;
            SendReply(pstReq, HTTP_BADMETHOD, "bad method", NULL);
        }
    }
    else
        std::cout << "no http request cmd." << std::endl;

    SendReply(pstReq, HTTP_OK, "200 OK", NULL);
}

int CMyHttpServer::Login(const struct evhttp_request* pstReq, const string& data, void* param)
{
    std::cout << "recv login request..." << std::endl;
    //TODO:登录
    return -1;
}

int CMyHttpServer::Logout(const struct evhttp_request* pstReq, const string& data, void* param)
{
    std::cout << "recv logout request..." << std::endl;
    //TODO:登出
    return -1;
}

int CMyHttpServer::Heartbeat(const struct evhttp_request* pstReq, const string& data, void* param)
{
    std::cout << "recv hreatbeat request..." << std::endl;
    //TODO:心跳
    return -1;
}

MyHttpServer.cpp(更新)

#include"MyDefine.h"
#include"CMyHttpServer.h"

int main()
{
#ifdef WIN32
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif

    std::cout << "Hello MyHttpServer!\n";

	CMyHttpServerMgr myHttpServerMgr(HTTP_SERVER_LISTEN_PORT);

	myHttpServerMgr.Start();
	while (true)
	{
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
	myHttpServerMgr.Stop();
	std::cout << "Stop MyHttpServer!\n";
	system("pause");

#ifdef WIN32
	WSACleanup();
#endif
}

运行调测

使用postman发送登录和登出请求,在打印输出上可以看到,请求被分配到了不同的线程上去执行处理。

基于libevent的C++高并发、易扩展HTTP服务迭代之路

我的机器是8核心数,所以创建了16个HTTP Server后台工作线程。

四 第二版代码的思考

1 HTTP Server适用于客户端主动向服务端发起请求的场景,如果我的服务端程序,除了被动应答请求,还想主动向客户端发起通知怎么办?

 解决办法:添加Websocket服务端模块。

2 如果我的HTTP Server想把接收到的HTTP请求,透传转发给系统内的其它服务模块,该怎么办呢?

 解决办法:添加HTTP客户端模块,建议使用libcurl。

3 如果我想在linux或IOS或Arm等系统上使用基于libevent的HTTP Server怎么办?

 解决办法:libevent本身是支持跨平台的。示例工程MyHttpServer仅支持Windows平台,对其进行跨平台移植便可以了。

4 除了HTTP之外,我还想支持HTTPS,怎么办呢?

 解决办法:libevent编译好了后,可以看到有个libevent_openssl.lib,将它引入工程。

基于libevent的C++高并发、易扩展HTTP服务迭代之路

由于个人水平有限,文章或代码中难免出现不当或缺陷的地方,欢迎您指正出来。

同时对于MyHTTPServer有其它什么好的建议或优化措施,也可以提出来沟通交流。感谢您的耐心阅读!文章来源地址https://www.toymoban.com/news/detail-410780.html

到了这里,关于基于libevent的C++高并发、易扩展HTTP服务迭代之路的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [C++历练之路]优先级队列||反向迭代器的模拟实现

    W...Y的主页 😊  代码仓库分享💕 🍔前言: 在C++的宇宙中,优先队列似乎是一座巨大的宝库,藏匿着算法的珍宝。而就在这片代码的天空下,我们不仅可以探索优先队列的神奇,还能够揭开反向迭代器的神秘面纱。让我们一同踏入这个编程的探险之旅,在这里,我们将用C

    2024年02月04日
    浏览(51)
  • 【实战项目】c++实现基于reactor的高并发服务器

    基于Reactor的高并发服务器,分为反应堆模型,多线程,I/O模型,服务器,Http请求和响应五部分 ​全局 Channel 描述了文件描述符以及读写事件,以及对应的读写销毁回调函数,对应存储arg读写回调对应的参数 ​Channel 异或 |:相同为0,异为1 按位与:只有11为1,其它组合全部

    2024年02月12日
    浏览(53)
  • 基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpRequest模块 解析http请求协议

    一、HTTP响应报文格式 二、根据解析出的原始数据,对客户端的请求做出处理  processHttpRequest  1.解码字符串   解决浏览器无法访问带特殊字符的文件得到问题 2.判断文件扩展名,返回对应的 Content-Type(Mime-Type) 3.发送文件  sendFile 4.发送目录 三、解析http请求协议  parseHttpR

    2024年02月02日
    浏览(54)
  • 服务端⾼并发分布式结构演进之路

    应⽤(Application)/系统(System) 为了完成一整套服务的一个程序或相互配合的程序群 模块(Module)/组件(Component) 当应⽤较复杂时,为了分离职责,将其中具有清晰职责的、内聚性强的部分,抽象出概念,便于理解 分布式(Distributed) 分布式(Distributed)是指将计算、任务

    2024年02月13日
    浏览(95)
  • 基于多反应堆的高并发服务器【C/C++/Reactor】(中)在TcpConnection 中接收并解析Http请求消息

    一、在TcpConnection 中多添加和http协议相关的request和response 二、给客户端回复数据(方法一) 1.在 Buffer.h 文件中添加 bufferSendData 函数:  2.在 TcpConnection.c 文件中添加 processWrite 函数: 3.修改 tcpConnectionInit 函数中调用的 channelInit函数 的写回调函数为 processWrite函数 三、给客户端

    2024年01月23日
    浏览(51)
  • 基于C++的简单HTTP服务器实现

    基于C++的简单HTTP服务器实现 一个Web Server就是一个服务器软件(程序),或者是运行这个服务器软件的硬件(计算机)。其主要功能是通过HTTP协议与客户端(通常是浏览器(Browser))进行通信,来接收,存储,处理来自客户端的HTTP请求,并对其请求做出HTTP响应,返回给客户

    2024年02月02日
    浏览(52)
  • 软件架构之前后端分离架构&服务器端高并发演进之路

    首先,当一个项目刚起步的时候,不能预知后续的访问量和并发能达到多少,在初步开发的架构选型中,采用前后端分离的架构。关于前后端分离架构,其优势如下: 前后端分离是一种软件系统架构模式,它将应用程序的前端和后端分离开发和部署。在这种架构中,前端和后

    2024年02月10日
    浏览(57)
  • 【libevent】http客户端3:简单封装

    LibEventHttp

    2024年02月15日
    浏览(52)
  • 基于 HTTP Range 实现文件分片并发下载!

    目录 前言 基础下载功能 进阶下载功能 单片下载 多片下载  浏览器发送预检(preflight)请求 express 不支持多段 range multipart/** 搭配 boundary=** 分片下载功能 “只读的” ArrayBuffer 对象 DataView 子类 Uint8Array 操作二进制数据 Blob + createObjectURL 创建 url 全部代码 通用的文件分片下载

    2024年02月10日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包