一、libevent evhttp使用
1、基本流程
http服务端使用到的借口函数及流程如下
1)、创建event_base和evhttp
struct event_base *event_base_new(void);
struct evhttp *evhttp_new(struct event_base *base);
2)、绑定地址和端口
int evhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port);
3)、设置处理函数
void evhttp_set_gencb(struct evhttp *http,
void (*cb)(struct evhttp_request *, void *), void *arg);
4)、派发事件循环
int event_base_dispatch(struct event_base *);
代码:
#include "event2/http.h"
#include "event2/http_struct.h"
#include "event2/event.h"
#include "event2/buffer.h"
#include "event2/dns.h"
#include "event2/thread.h"
#include <stdlib.h>
#include <stdio.h>
void HttpGenericCallback(struct evhttp_request* request, void* arg)
{
const struct evhttp_uri* evhttp_uri = evhttp_request_get_evhttp_uri(request);
char url[8192];
evhttp_uri_join(const_cast<struct evhttp_uri*>(evhttp_uri), url, 8192);
printf("accept request url:%s\n", url);
struct evbuffer* evbuf = evbuffer_new();
if (!evbuf)
{
printf("create evbuffer failed!\n");
return ;
}
//HTTP header
evhttp_add_header(request->output_headers, "Server", "myhttp v1.0");
evhttp_add_header(request->output_headers, "Content-Type", "text/plain; charset=UTF-8");//utf8
//evhttp_add_header(request->output_headers, "Content-Type", "text/html");
evhttp_add_header(request->output_headers, "Access-Control-Allow-Origin", "*");//跨域
evhttp_add_header(request->output_headers, "Connection", "close");
evbuffer_add_printf(evbuf, "Server response. Your request url is %s", url);
evhttp_send_reply(request, HTTP_OK, "OK", evbuf);
evbuffer_free(evbuf);
}
void specific_handler(struct evhttp_request *req, void *arg)
{
}
int main(int argc, char** argv)
{
if (argc != 2)
{
printf("usage:%s port\n", argv[0]);
return 1;
}
int port = atoi(argv[1]);
if (port == 0)
{
printf("port error:%s\n", argv[1]);
return 1;
}
struct event_base* base = event_base_new();
if (!base)
{
printf("create event_base failed!\n");
return 1;
}
struct evhttp* http = evhttp_new(base);
if (!http)
{
printf("create evhttp failed!\n");
return 1;
}
if (evhttp_bind_socket(http, "0.0.0.0", port) != 0)
{
printf("bind socket failed! port:%d\n", port);
return 1;
}
int http_option_timeout = 120; //in seconds
evhttp_set_timeout(http, http_option_timeout);
//evhttp_set_allowed_methods( http , EVHTTP_REQ_GET);
//指定generic callback
evhttp_set_gencb(http, HttpGenericCallback, NULL);
//Set a callback for a specified URI
evhttp_set_cb(http, "/spec", specific_handler, NULL);
event_base_dispatch(base);
evhttp_free(http);
return 0;
}
多线程的Http Server
两种方式:
1、启多个线程,每个子线程持有一个event_base和evhttp,接受请求和处理业务逻辑在同一个子线程中。
2、主线程持有一个event_base和evhttp,主线程接受到请求放入队列,子线程中只处理业务逻辑。
在上面的Http Server中,处理Http请求的回调函数generic_handler和定时器读取文件的回调函数read_file_timer_cb都在同一个event_base的dispatch中,并且都在同一个进程中,使用多线程可以改善程序的性能,下面是一个来自网络的多线程Http Server:
#include <event.h>
#include <evhttp.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
int httpserver_bindsocket(int port, int backlog);
int httpserver_start(int port, int nthreads, int backlog);
void* httpserver_Dispatch(void *arg);
void httpserver_GenericHandler(struct evhttp_request *req, void *arg);
void httpserver_ProcessRequest(struct evhttp_request *req);
int httpserver_bindsocket(int port, int backlog) {
int r;
int nfd;
nfd = socket(AF_INET, SOCK_STREAM, 0);
if (nfd < 0) return -1;
int one = 1;
r = setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(int));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
r = bind(nfd, (struct sockaddr*)&addr, sizeof(addr));
if (r < 0) return -1;
r = listen(nfd, backlog);
if (r < 0) return -1;
int flags;
if ((flags = fcntl(nfd, F_GETFL, 0)) < 0
|| fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
return -1;
return nfd;
}
int httpserver_start(int port, int nthreads, int backlog) {
int r, i;
int nfd = httpserver_bindsocket(port, backlog);
if (nfd < 0) return -1;
pthread_t ths[nthreads];
for (i = 0; i < nthreads; i++) {
struct event_base *base = event_init();
if (base == NULL) return -1;
struct evhttp *httpd = evhttp_new(base);
if (httpd == NULL) return -1;
r = evhttp_accept_socket(httpd, nfd);
if (r != 0) return -1;
evhttp_set_gencb(httpd, httpserver_GenericHandler, NULL);
r = pthread_create(&ths[i], NULL, ht