在项目或产品开发中,需要用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
- #include
- #include
- #include
- #include
- #include
- #include
-
- #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中的项目工程截图,有个直观的总体认识。

头文件包含目录

Lib库目录

引入Lib文件

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

登出请求

控制台打印输出

对于并发量要求不高、业务请求种类不多的场景,单线程的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的定义格式,有没有什么规律:

红线框中的路径都是一样的,就后边的方法名称不一样。把方法名称当做关键字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发送登录和登出请求,在打印输出上可以看到,请求被分配到了不同的线程上去执行处理。

我的机器是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,将它引入工程。

由于个人水平有限,文章或代码中难免出现不当或缺陷的地方,欢迎您指正出来。
同时对于MyHTTPServer有其它什么好的建议或优化措施,也可以提出来沟通交流。感谢您的耐心阅读!