libevent 的http模块实现http服务器

        首先声明,libevent的http模块是为单线程设计的,如果业务逻辑中有耗时操作,则需要自行设计线程池以便提高吞吐量,每个工作线程中都要运行一个event_base_loop和一个evhttp实例(这些evhttp实例需要用evhttp_bind_socket绑定到相同的端口上),具体参考官方issue。(PS: libevent版本为2.1.12)

https://github.com/libevent/libevent/issues/997
https://github.com/libevent/libevent/issues/1001

        其次,evhttp 使用http1.1, 默认keep-alive是打开状态。连接成功的客户端连接被保存在evhttp的connections列表中,这些连接的事件由同一个event_base监管,并根据优先级串行处理,所以如果有耗时操作,将会严重拉低服务器性能。但是对于并发量不是很高的场景,单线程evhttp已经足够使用了。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#ifndef _WIN32
#include 
#endif
#include 
#include 
using namespace std;
#define WEBROOT "." 
#define DEFAULTINDEX "index.html"

void offline_callback(struct evhttp_connection* evcon, void* arg)
{
	struct evhttp_request* req = (struct evhttp_request*)arg;
	if (req)
	{
		if (req->uri != NULL)
		{
			char* orignal_uri = evhttp_decode_uri(req->uri);
			if (orignal_uri)
			{
				cout << "resquest uri:"<
///  http请求的回调函数
///  需要注意的方面:
///		1. 每进来一个请求,在该请求被回复之前,对应的EV_READ事件会被禁用,回复之后会重新打开EV_READ事件。
///		2. 每一个请求都要回复,即使请求有误也应该回复一个错误信息。以便及时打开EV_READ。
///		3. 如果EV_READ没打开,则心跳包也会失效,无法及时关掉失效的连接,造成内存泄露。
///		4. libevent http这种机制,在http pipeline模式下,会让底层TCP协议栈的发送方因为接收方读取过慢而阻塞。
/// 
/// 
/// 
void http_cb(struct evhttp_request* request, void* arg)
{
	//1 获取浏览器的请求信息
	const char* uri = evhttp_request_get_uri(request);
	cout << "uri:" << uri << endl;
	string cmdtype;
	switch (evhttp_request_get_command(request))
	{
	case EVHTTP_REQ_GET:
		cmdtype = "GET";
		break;
	case EVHTTP_REQ_POST:
		cmdtype = "POST";
		break;
	}
	cout << "cmdtype:" << cmdtype << endl;
	// 消息报头
	evkeyvalq* headers = evhttp_request_get_input_headers(request);
	cout << "====== headers ======" << endl;
	for (evkeyval* p = headers->tqh_first; p != NULL; p = p->next.tqe_next)
		cout << p->key << ":" << p->value << endl;
	
	// 读取客户端发来的请求正文 (GET为空,POST有表单信息  )
	evbuffer* inbuf = evhttp_request_get_input_buffer(request);
	char buf[1024] = { 0 };
	cout << "======= Input data ======" << endl;
	while (evbuffer_get_length(inbuf))
	{
		int n = evbuffer_remove(inbuf, buf, sizeof(buf) - 1);
		if (n > 0)
		{
			buf[n] = '\0';
			cout << buf << endl;
		}
	}

	//2 回复浏览器
	// 状态行 消息报头 响应正文
	string filepath = WEBROOT;
	filepath += uri;
	if (strcmp(uri, "/") == 0)
	{
		//默认加入首页文件
		filepath += DEFAULTINDEX;
	}
	//消息报头

	//读取html文件返回正文
	FILE* fp = fopen(filepath.c_str(), "rb");
	if (!fp)
	{
		evhttp_send_reply(request, HTTP_NOTFOUND, "", 0);
		return;
	}
	evbuffer* outbuf = evhttp_request_get_output_buffer(request);
	for (;;)
	{
		int len = fread(buf, 1, sizeof(buf), fp);
		if (len <= 0)break;
		evbuffer_add(outbuf, buf, len);
	}
	fclose(fp);
	evhttp_send_reply(request, HTTP_OK, "", outbuf);

	//3. 连接相关参数更新
	struct evhttp_connection* conn = evhttp_request_get_connection(request);
	bufferevent* bev = evhttp_connection_get_bufferevent(conn);
	int fd = bufferevent_getfd(bev);
	struct event_base* base = evhttp_connection_get_base(conn);
	//设置(更新)连接的存活时间(超时会发送心跳包探测)  默认时间为50
	struct timeval timeout = {10,0};
	bufferevent_set_timeouts(bev, &timeout,NULL);
	// 检测到客户端关闭或者网络断开时,会触发evhttp_error_cb函数
	// evhttp_error_cb内部会执行evhttp_connection_free,进而调用offline_callback
	evhttp_connection_set_closecb(conn, offline_callback, request);
}
int main()
{
#ifdef _WIN32 
	//初始化socket库
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);
#else
	//忽略SIGPIPE信号
	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		return 1;
#endif

	std::cout << "test server!\n";
	//创建libevent的上下文
	event_base* base = event_base_new();
	if (base)
		cout << "event_base_new success!" << endl;

	// http  服务器
	//1	创建evhttp上下文
	evhttp* http = evhttp_new(base);

	//2  绑定端口和IP
	struct evhttp_bound_socket* handle;
	handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", 9799);
	if (!handle) {
		cout << "evhttp_bind_socket failed!" << endl;
	}

	//3   设定回调函数
	evhttp_set_gencb(http, http_cb, 0);
	//evhttp_set_bevcb();
	//evhttp_set_cb();
	if (base)
		event_base_dispatch(base);
	if (base)
		event_base_free(base);
	if (http)
		evhttp_free(http);
#ifdef _WIN32
	WSACleanup();
#endif
	return 0;
}

你可能感兴趣的:(服务器,http)