基于libevent实现http服务端和客户端

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&param2=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, &params, "param1");
    char *param2_value = findGetParam(request, &params, "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&param2=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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值