c++基于libevent库实现的客户端和服务端(GET和POST)
头文件:http_event.hpp
#ifndef __HTTP_EVENT_HPP__
#define __HTTP_EVENT_HPP__
#include <iostream>
#include <sstream>
#include "evhttp.h"
#include "event.h"
using namespace std;
// ev http server
class evHttpServer
{
private:
string ip;
int port;
struct evhttp *http_server = NULL;
struct event_base *base = NULL;
public:
evHttpServer(string ip, int port) : ip(ip), port(port){};
// 创建http 绑ip端口
bool serverConnect();
// 设置服务超时时间
void setHttpTimeout(int timeout);
/*
接口注册
@param path 接口路径 如"/get_task"
@param cb 处理该接口的回调
@param cb_arg 回调的参数
*/
void interfaceRegister(const char *path,
void (*cb)(struct evhttp_request *, void *),
void *cb_arg = NULL);
// 循环监听
void loopListen();
~evHttpServer();
public:
/*
http响应
@param code - 响应码
@param reason - 响应码对应的说明, 为NULL,使用默认的响应码说明
@param body - body, 为NULL, 没有body
*/
static void reply(evhttp_request *request, int code, const char *reason, const char *body);
// Get回调函数例子
static void cbGETSample(struct evhttp_request *request, void *arg);
// 解析request获取GET请求参数
static char *findGetParam(struct evhttp_request *request, struct evkeyvalq *params, const char *query_char);
// Post回调函数例子
static void cbPOSTSample(struct evhttp_request *request, void *arg);
};
// ev http client
class evhttpClient
{
private:
string ip;
int port;
string path = "";
string body = "";
int timeout = 10;
public:
/*
@param path 资源路径可以包含GET请求参数部分
如"/getparam?param1=123456¶m2=456789"
*/
evhttpClient(string ip, int port, string path);
// 直接传入URL进行初始化
evhttpClient(string url);
// 添加GET请求参数
void addGetParam(string name, string value);
// 设置包体
void setBody(string body);
// 返回请求的url
string getUrl();
// 设置请求超时时间
void setTimeout(int timeout);
/*
发送GET/POST请求
@param type http请求类型
EVHTTP_REQ_GET 1
EVHTTP_REQ_POST 2
*/
bool sendHttpRequest(evhttp_cmd_type type);
~evhttpClient();
private:
// 请求回调函数,可以自行在类外实现
static void requestCallback(struct evhttp_request *request, void *arg);
};
#endif
源文件:http_event.cpp
#include "http_event.hpp"
/*
服务端
------------------------------------------------------------------------------------
*/
bool evHttpServer::serverConnect()
{
base = event_base_new();
if (!base)
{
fprintf(stderr, "Create event_base failed.\n");
return false;
}
http_server = evhttp_new(base);
if (http_server == NULL)
{
fprintf(stderr, "Create evhttp failed.\n");
return false;
}
if (evhttp_bind_socket(http_server, ip.c_str(), port) != 0)
{
fprintf(stderr, "bind socket<%s:%d> failed.\n", ip.c_str(), port);
return false;
}
return true;
}
void evHttpServer::setHttpTimeout(int timeout)
{
if (http_server)
{
evhttp_set_timeout(http_server, timeout);
}
}
void evHttpServer::interfaceRegister(const char *path, void (*cb)(evhttp_request *, void *), void *cb_arg)
{
if (http_server)
{
evhttp_set_cb(http_server, path, cb, cb_arg);
}
}
void evHttpServer::loopListen()
{
if (base)
{
event_base_dispatch(base);
}
}
evHttpServer::~evHttpServer()
{
if (http_server)
{
evhttp_free(http_server);
}
}
void evHttpServer::cbGETSample(evhttp_request *request, void *arg)
{
if (request == NULL)
{
return;
}
struct evkeyvalq params = {0};
char *param1_value = findGetParam(request, ¶ms, "param1");
char *param2_value = findGetParam(request, ¶ms, "param2");
if (param1_value == NULL || param2_value == NULL)
{
evhttp_send_error(request, HTTP_BADREQUEST, NULL); // reason为NULL时会发送默认的error描述
}
else
{
cout << "param1:" << param1_value << endl;
cout << "param2:" << param2_value << endl;
/*
对value做处理
*/
// 响应体
reply(request, HTTP_OK, "Success", "this is body");
}
}
char *evHttpServer::findGetParam(struct evhttp_request *request, struct evkeyvalq *params, const char *query_char)
{
if (request == NULL || params == NULL || query_char == NULL)
{
return NULL;
}
// 返回uri(request->uri) (/getparam?param1=hello) / 往后的部分
// const char *uri = evhttp_request_get_uri(request);
// 解析参数部分
struct evhttp_uri *ev_uri = evhttp_uri_parse(request->uri); // 需要evhttp_uri_free释放
if (!ev_uri)
{
fprintf(stderr, "evhttp_uri_parse uri failed!\n");
return NULL;
}
// 返回(ev_uri->query) URI中的查询参数部分 ? 往后的部分 param1=hello
const char *query_param = evhttp_uri_get_query(ev_uri);
if (query_param == NULL)
{
fprintf(stderr, "evhttp_uri_parse uri failed!\n");
evhttp_uri_free(ev_uri);
return NULL;
}
// 查询指定参数的值 应该是将query_param 赋给 params
evhttp_parse_query_str(query_param, params);
// (params是传入的结构) query_result应该是params的一部分 否则无法正常返回
char *query_result = (char *)evhttp_find_header(params, query_char);
evhttp_uri_free(ev_uri);
return query_result;
}
void evHttpServer::cbPOSTSample(evhttp_request *request, void *arg)
{
if (request == NULL)
{
return;
}
// 获取post body长度
size_t body_size = evbuffer_get_length(request->input_buffer);
if (body_size <= 0)
{
fprintf(stderr, "POST Body is null\n");
evhttp_send_error(request, HTTP_BADMETHOD, "POST Body is null");
}
else
{
fprintf(stdout, "POST Body len(%ld)\n", body_size);
// evbuffer_pullup函数是移动指定字节数的数据到huan缓冲区的起始位置
// 所以取body的时候需要取指定的大小
string buff = (char *)evbuffer_pullup(request->input_buffer, -1);
string body = buff.substr(0, body_size);
fprintf(stdout, "Body:[%s]\n", body.c_str());
/*
处理post请求
*/
reply(request, HTTP_OK, NULL, NULL);
}
}
void evHttpServer::reply(evhttp_request *request, int code, const char *reason, const char *body)
{
struct evbuffer *retbuff = NULL;
if (body)
{
retbuff = evbuffer_new();
evbuffer_add_printf(retbuff, "%s", body);
}
evhttp_send_reply(request, code, reason, retbuff); // reason为NULL 发送code默认reason
if (body)
evbuffer_free(retbuff);
}
/*
客户端
------------------------------------------------------------------------------------
*/
evhttpClient::evhttpClient(string ip, int port, string path) : ip(ip), port(port), path(path){};
evhttpClient::evhttpClient(string url)
{
struct evhttp_uri *ev_uri = evhttp_uri_parse(url.c_str());
if (!ev_uri)
{
fprintf(stderr, "parse url failed!\n");
return;
}
const char *host = evhttp_uri_get_host(ev_uri); // IP
int p = evhttp_uri_get_port(ev_uri); // port
const char *spath = evhttp_uri_get_path(ev_uri); // 目录 /tasks
const char *param = evhttp_uri_get_query(ev_uri); // 获取参数 param1=hello1¶m2=hello2
ip = host;
port = p;
if (spath == NULL)
{
path = "/";
}
else
{
if (param == NULL)
{
path = spath;
}
else
{
string sparam = param;
if (sparam == "") // 参数为空, 不是null
{
path = spath;
}
else
{
stringstream ss;
ss << spath << "?" << param;
path = ss.str();
}
}
}
evhttp_uri_free(ev_uri);
}
void evhttpClient::addGetParam(string name, string value)
{
if (path.find("?") != string::npos) // 要添加&
{
path = path + "&" + name + "=" + value;
}
else // 第一个添加 需要?
{
path = path + "?" + name + "=" + value;
}
}
void evhttpClient::setBody(string body)
{
this->body = body;
}
string evhttpClient::getUrl()
{
return "http://" + ip + ":" + to_string(port) + path;
}
void evhttpClient::setTimeout(int timeout)
{
this->timeout = timeout;
}
bool evhttpClient::sendHttpRequest(evhttp_cmd_type type)
{
// 创建libevent上下文
struct event_base *base = event_base_new();
if (!base)
{
fprintf(stderr, "Error creating event base\n");
return false;
}
// 创建HTTP客户端
struct evhttp_connection *conn = evhttp_connection_base_new(base, NULL, ip.c_str(), port);
if (!conn)
{
fprintf(stderr, "Error creating HTTP connection\n");
event_base_free(base);
return false;
}
// 创建HTTP请求
struct evhttp_request *req = evhttp_request_new(requestCallback, base);
if (!req)
{
fprintf(stderr, "Error creating HTTP request\n");
evhttp_connection_free(conn);
event_base_free(base);
return false;
}
if (type == EVHTTP_REQ_POST)
{
/* add body */
evbuffer_add(req->output_buffer, body.c_str(), body.length());
}
// 发送请求
evhttp_make_request(conn, req, type, path.c_str());
evhttp_connection_set_timeout(req->evcon, timeout);
// 运行事件循环
event_base_dispatch(base);
// 释放资源
evhttp_connection_free(conn);
event_base_free(base);
return true;
}
evhttpClient::~evhttpClient() {}
void evhttpClient::requestCallback(evhttp_request *request, void *arg)
{
if (request == NULL)
{
fprintf(stderr, "Get Request failed, is NULL");
return;
}
// 获取响应状态码 和 reason
int response_code = evhttp_request_get_response_code(request);
std::cout << "Response Code: " << response_code << " "
<< evhttp_request_get_response_code_line(request) << std::endl;
// 获取数据长度
size_t recv_size = evbuffer_get_length(request->input_buffer);
fprintf(stdout, "Recv Body len(%ld)\n", recv_size);
if (recv_size > 0)
{
// evbuffer_pullup函数是移动指定字节数的数据到huan缓冲区的起始位置
// 所以取body的时候需要取指定的大小
string buff = (char *)evbuffer_pullup(request->input_buffer, -1);
string body = buff.substr(0, recv_size);
fprintf(stdout, "Recv Body:[%s]\n", body.c_str());
}
/*
event_base_loopexit 函数用于设置一个计数器,告诉事件循环在所有计数器减为0时退出事件循环。
需要调用 event_base_loopexit 设置计数器的值,并且在每个事件处理完毕时调用 event_base_loopexit 减少计数器的值。
当计数器减为0时,事件循环会退出。
*/
event_base_loopexit((struct event_base *)arg, NULL);
/*
event_base_loopbreak 函数用于在事件循环中某一次迭代结束后,即使有激活的事件存在,也强制退出事件循环,使事件循环提前终止。
这个函数会在当前迭代结束后尽快退出事件循环,而不是等待所有的事件都被处理完毕。
使用 event_base_loopbreak 可以在需要时强制终止事件循环,即使有未处理完的事件存在。
*/
// event_base_loopbreak((struct event_base*)arg);
}
#if 0
// main test server
int main()
{
evHttpServer server("0.0.0.0", 7890);
if (!server.serverConnect())
{
return -1;
}
server.setHttpTimeout(10);
server.interfaceRegister("/getparam", evHttpServer::cbGETSample);
server.interfaceRegister("/setparam", evHttpServer::cbPOSTSample);
server.loopListen();
}
#endif
#if 0
// main test client
int main()
{
// evhttpClient client("http://127.0.0.1:7890/getparam?param1=hello1");
// client.addGetParam("param2", "hello2");
// cout << client.getUrl() << endl;
// client.setTimeout(3);
// cout << "send GET request" << endl;
// client.sendHttpRequest(EVHTTP_REQ_GET);
evhttpClient client("http://127.0.0.1:7890/setparam");
string body = "{\"id\":1001,\"name\":\"张三\"}";
client.setBody(body);
cout << "send POST request" << endl;
client.setTimeout(3);
client.sendHttpRequest(EVHTTP_REQ_POST);
}
#endif