libevent从入门到掌握六

libevent从入门到掌握<六>


一、前言

​ 在libevent中,封装了http模块,包括很多相关的接口,主要记录一下简单的http使用方法和接口源码内容;

​ 创建http方式有很多种,这里只是一种简单的方式,仅供参考;

http程序创建步骤:

1、初始化event模块

2、启动http服务端

3、设置事件处理函数

4、监听事件


二、接口说明

1、 event_init()

不做过多说名,前面文章中有介绍和说明;


2、evhttp_start()

/*
 * Start a web server on the specified address and port.
 */

struct evhttp *
evhttp_start(const char *address, ev_uint16_t port)
{
	struct evhttp *http = NULL;
	//初始化结构体evhttp
	http = evhttp_new_object();
	if (http == NULL)
		return (NULL);
	//绑定监听地址和端口
	if (evhttp_bind_socket(http, address, port) == -1) {
		mm_free(http);
		return (NULL);
	}

	return (http);
}

初始化中调用了evhttp_new_object()函数:

static struct evhttp*
evhttp_new_object(void)
{
	struct evhttp *http = NULL;
	//申请内存
	if ((http = mm_calloc(1, sizeof(struct evhttp))) == NULL) {
		event_warn("%s: calloc", __func__);
		return (NULL);
	}
	//清空时间延时
	evutil_timerclear(&http->timeout_read);
	evutil_timerclear(&http->timeout_write);
	//设置包头和包体的最大值
	evhttp_set_max_headers_size(http, EV_SIZE_MAX);
	evhttp_set_max_body_size(http, EV_SIZE_MAX);
	evhttp_set_default_content_type(http, "text/html; charset=ISO-8859-1");
	//设置支持的操作类型
	evhttp_set_allowed_methods(http,
	    EVHTTP_REQ_GET |
	    EVHTTP_REQ_POST |
	    EVHTTP_REQ_HEAD |
	    EVHTTP_REQ_PUT |
	    EVHTTP_REQ_DELETE);

	TAILQ_INIT(&http->sockets);
	TAILQ_INIT(&http->callbacks);
	TAILQ_INIT(&http->connections);
	TAILQ_INIT(&http->virtualhosts);
	TAILQ_INIT(&http->aliases);

	return (http);
}

struct evhttp *
evhttp_new(struct event_base *base)
{
	struct evhttp *http = NULL;

	http = evhttp_new_object();
	if (http == NULL)
		return (NULL);
	http->base = base;

	return (http);
}

/*
 * Start a web server on the specified address and port.
 */

struct evhttp *
evhttp_start(const char *address, ev_uint16_t port)
{
	struct evhttp *http = NULL;
	//初始化结构体evhttp
	http = evhttp_new_object();
	if (http == NULL)
		return (NULL);
	//绑定监听地址和端口
	if (evhttp_bind_socket(http, address, port) == -1) {
		mm_free(http);
		return (NULL);
	}

	return (http);
}

开启服务中,最后一步是绑定和监听IP和端口:

int
evhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port)
{
	//什么也没做,直接调用了evhttp_bind_socket_with_handle()函数
	struct evhttp_bound_socket *bound =
		evhttp_bind_socket_with_handle(http, address, port);
	if (bound == NULL)
		return (-1);
	return (0);
}

struct evhttp_bound_socket *
evhttp_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port)
{
	evutil_socket_t fd;
	struct evhttp_bound_socket *bound;
	int serrno;
	//申请socket,bind,绑定端口
	if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1)
		return (NULL);

	if (listen(fd, 128) == -1) {
		serrno = EVUTIL_SOCKET_ERROR();
		event_sock_warn(fd, "%s: listen", __func__);
		evutil_closesocket(fd);
		EVUTIL_SET_SOCKET_ERROR(serrno);
		return (NULL);
	}
	//accept 处理
	bound = evhttp_accept_socket_with_handle(http, fd);

	if (bound != NULL) {
		event_debug(("Bound to port %d - Awaiting connections ... ",
			port));
		return (bound);
	}

	return (NULL);
}

3、evhttp_set_timeout()

void
evhttp_set_timeout(struct evhttp* http, int timeout)
{
	//设置读超时
	evhttp_set_timeout_(&http->timeout_read,  timeout, -1);
	//设置写超时
	evhttp_set_timeout_(&http->timeout_write, timeout, -1);
}

4、evhttp_set_cb()

int
evhttp_set_cb(struct evhttp *http, const char *uri,
    void (*cb)(struct evhttp_request *, void *), void *cbarg)
{
	struct evhttp_cb *http_cb;

	TAILQ_FOREACH(http_cb, &http->callbacks, next) {
		if (strcmp(http_cb->what, uri) == 0)
			return (-1);
	}
	//申请内存
	if ((http_cb = mm_calloc(1, sizeof(struct evhttp_cb))) == NULL) {
		event_warn("%s: calloc", __func__);
		return (-2);
	}

	http_cb->what = mm_strdup(uri);
	if (http_cb->what == NULL) {
		event_warn("%s: strdup", __func__);
		mm_free(http_cb);
		return (-3);
	}
	http_cb->cb = cb;
	http_cb->cbarg = cbarg;
	//插入事件列表中
	TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next);

	return (0);
}

5、event_dispatch()

int
event_dispatch(void)
{
	//调用event_loop
	return (event_loop(0));
}

int
event_loop(int flags)
{
	//调用event_base_loop
	return event_base_loop(current_base, flags);
}

参考之前的文章event_base_loop(),循环检测事件的发生;


三、简单案例
#include <stdio.h>
#include <stdlib.h>
#include <evhttp.h>
#include <event.h>
#include <string.h>
#include "event2/http.h"
#include "event2/event.h"
#include "event2/buffer.h"
#include "event2/bufferevent.h"
#include "event2/bufferevent_compat.h"
#include "event2/http_struct.h"
#include "event2/http_compat.h"
#include "event2/util.h"
#include "event2/listener.h"


void Test_Get(struct evhttp_request *req,void *arg)
{
	{
		//解析头,进行处理
		//这里只做了回复	
	}
	struct evbuffer* retbuff = evbuffer_new();
	if(NULL == retbuff)
	{
		printf("retbuff is NULL\n");
		return ;
	}
	evbuffer_add_printf(retbuff,"Test_Get is OK!");
	evhttp_send_reply(req,HTTP_OK,"Client is OK",retbuff);
	evbuffer_free(retbuff);
}
void Test_Post(struct evhttp_request *req,void *arg)
{
	{
		//获取数据,然后进行处理
		//evbuffer_pullup()获取数据接口
	}
	//只做回复
	struct evbuffer* retbuff = evbuffer_new();
	if(NULL == retbuff)
	{
		printf("retbuff is NULL\n");
		return ;
	}
	evbuffer_add_printf(retbuff,"Test_post is OK!");
	evhttp_send_reply(req,HTTP_OK,"Client is OK",retbuff);
	evbuffer_free(retbuff);
}

int main()
{
    short http_port = 8081;
    char *http_addr = "127.0.0.1";

    //初始化
    event_init();
    //启动http服务端
    struct evhttp *http_server = evhttp_start(http_addr,http_port);
    if(http_server == NULL)
    {
        printf("====line:%d,%s\n",__LINE__,"http server start failed.");
        return -1;
    }

    //设置请求超时时间(s)
    evhttp_set_timeout(http_server,5);
    //设置事件处理函数,evhttp_set_cb针对每一个事件(请求)注册一个处理函数,
    //区别于evhttp_set_gencb函数,是对所有请求设置一个统一的处理函数
    evhttp_set_cb(http_server,"/hello/post",Test_Post,NULL);
    evhttp_set_cb(http_server,"/hello/get",Test_Get,NULL);

    //循环监听
    event_dispatch();
    evhttp_free(http_server);

    return 0;
}

结果:

在这里插入图片描述
在这里插入图片描述


想了解学习更多C++后台服务器方面的知识,请关注:
微信公众号:C++后台服务器开发


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值