基于Libevent的HTTP Server

简单的Http Server

使用Libevent内置的http相关接口,可以很容易的构建一个Http Server,一个简单的Http Server如下:


#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/http.h>
#include <Winsock2.h>
#include <stdlib.h>
#include <stdio.h>

int init_win_socket()
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2) , &wsaData) != 0)
{
return -1;
}
return 0;
}

void generic_handler(struct evhttp_request *req, void *arg)
{
struct evbuffer *buf = evbuffer_new();
if(!buf)
{
puts("failed to create response buffer \n");
return;
}

evbuffer_add_printf(buf, "Server Responsed. Requested: %s\n", evhttp_request_get_uri(req));
evhttp_send_reply(req, HTTP_OK, "OK", buf);
evbuffer_free(buf);
}

int main(int argc, char* argv[])
{
#ifdef WIN32
init_win_socket();
#endif

short http_port = 8081;
char *http_addr = "127.0.0.1";

struct event_base * base = event_base_new();

struct evhttp * http_server = evhttp_new(base);
if(!http_server)
{
return -1;
}

int ret = evhttp_bind_socket(http_server,http_addr,http_port);
if(ret!=0)
{
return -1;
}

evhttp_set_gencb(http_server, generic_handler, NULL);

printf("http server start OK! \n");

event_base_dispatch(base);

evhttp_free(http_server);

WSACleanup();
return 0;
}

通过Libevent的接口构建一个Http Server的过程如下:

(1)初始化:在event_base上新建一个evhttp,将这个evhttp绑定到监听的IP和端口号。

(2)设置Http回调函数:使用evhttp_set_gencb设置Http Server的处理请求的回调函数。

(3)启动Http Server:等待请求进入事件循环。

在Http Server中使用定时器提供更新服务


#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/http.h>
#include <sys/stat.h>
#include <Winsock2.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define DEFAULT_FILE "F:\\Libevent\\LibeventTest\\Debug\\sample.txt"

char *filedata;
time_t lasttime = 0;
char filename[80];
int counter = 0;

struct event *loadfile_event;
struct timeval tv;

void read_file()
{
unsigned long size = 0;
char *data;
struct stat buf;

if(stat(filename,&buf)<0)
{
printf("Read file error! \n");
return;
}

if (buf.st_mtime > lasttime)
{
if (counter++)
fprintf(stderr,"Reloading file: %s",filename);
else
fprintf(stderr,"Loading file: %s",filename);

FILE *f = fopen(filename, "rb");
if (f == NULL)
{
fprintf(stderr,"Couldn‘t open file\n");
return;
}

size = buf.st_size;
filedata = (char *)malloc(size+1);
memset(filedata,0,size+1);
fread(filedata, sizeof(char), size, f);
fclose(f);

fprintf(stderr," (%d bytes)\n",size);
lasttime = buf.st_mtime;
}
}

void read_file_timer_cb(evutil_socket_t listener, short event, void *arg)
{
if (!evtimer_pending(loadfile_event, NULL))
{
event_del(loadfile_event);
evtimer_add(loadfile_event, &tv);
}

read_file();
}

void load_file(struct event_base * base)
{
tv.tv_sec = 5;
tv.tv_usec = 0;

//loadfile_event = malloc(sizeof(struct event));
loadfile_event = evtimer_new(base,read_file_timer_cb,NULL);

//evtimer_set(loadfile_event,load_file,loadfile_event);
evtimer_add(loadfile_event,&tv);
}

void generic_handler(struct evhttp_request *req, void *arg)
{
struct evbuffer *buf = evbuffer_new();
if(!buf)
{
puts("failed to create response buffer \n");
return;
}
evbuffer_add_printf(buf,"%s",filedata);
evhttp_send_reply(req, HTTP_OK, "OK", buf);
evbuffer_free(buf);
}

int init_win_socket()
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2) , &wsaData) != 0)
{
return -1;
}
return 0;
}

int main(int argc, char* argv[])
{
#ifdef WIN32
init_win_socket();
#endif

short http_port = 8081;
char *http_addr = "127.0.0.1";

if (argc > 1)
{
strcpy(filename,argv[1]);
printf("Using %s\n",filename);
}
else
{
strcpy(filename,DEFAULT_FILE);
}

struct event_base * base = event_base_new();

struct evhttp * http_server = evhttp_new(base);
if(!http_server)
{
return -1;
}

int ret = evhttp_bind_socket(http_server,http_addr,http_port);
if(ret!=0)
{
return -1;
}

evhttp_set_gencb(http_server, generic_handler, NULL);

read_file();

load_file(base);

printf("http server start OK! \n");

event_base_dispatch(base);

evhttp_free(http_server);

WSACleanup();
return 0;
}

在这个Http Server中提供了一个每5秒触发一次的定时器,读取一个文件,如果这个文件被更新过,则读取更新后的内容。

当访问这个Http Server时,提供这个文件中最新的内容。

多线程的Http Server

在上面的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, httpserver_Dispatch, base);
if (r != 0) return -1;
}
for (i = 0; i < nthreads; i++) {
pthread_join(ths[i], NULL);
}
}

void* httpserver_Dispatch(void *arg) {
event_base_dispatch((struct event_base*)arg);
return NULL;
}

void httpserver_GenericHandler(struct evhttp_request *req, void *arg) {
httpserver_ProcessRequest(req);
}

void httpserver_ProcessRequest(struct evhttp_request *req) {
struct evbuffer *buf = evbuffer_new();
if (buf == NULL) return;

//here comes the magic
}

int main(void) {
httpserver_start(80, 10, 10240);
}

上面的代码基于Libevent
1.X版本的,不过很容易很看懂:在一个监听socket上创建了多个event_base实例和evhttp实例,在不同的线程中调度不同的event_base,继而可以在不同的线程中处理http请求。

这里还有一个基于Libevent的多线程Http Server:https://sourceforge.net/projects/libevent-thread/,看源代码处理的过程和上面类似,只是每次在监听的socket上accept一个连接请求时,将对应的处理放到一个工作队列里,在队列里由多线程处理相应的回调函数。

基于Libevent的HTTP Server,布布扣,bubuko.com

时间: 2024-10-10 12:24:45

基于Libevent的HTTP Server的相关文章

基于 libevent 开发的 C++ 11 高性能网络服务器 evpp

evpp是一个基于libevent开发的现代化C++11高性能网络服务器,自带TCP/UDP/HTTP等协议的异步非阻塞式的服务器和客户端库. 特性: 现代版的C++11接口 非阻塞异步接口都是C++11的functional/bind形式的回调仿函数(不是libevent中的C风格的函数指针) 非阻塞纯异步多线程TCP服务器/客户端 非阻塞纯异步多线程HTTP服务器/客户端 非阻塞纯异步多线程UDP服务器 支持多进程模式 优秀的跨平台特性和高性能(继承自libevent的优点) 除此之外,基于

基于libevent的tcp拆包分包库

TCP/IP协议虽然方便,但是由于是基于流的传输(UDP是基于数据报的传输),无论什么项目,总少不了解决拆包分包问题. 以前的项目总是每个程序员自己写一套拆包分包逻辑,实现的方法与稳定性都不太一致.终于有了做基线的机会,自己写了一个基于libevent的拆包分包库. 本文档黏贴一些核心的内容. //回调接口 class ITcpPacketNotify{public:virtual void OnConnected(int fd) = 0;virtual void OnDisConnected(

SQL Server虚拟化系列(3)&mdash;&mdash;构建理想的基于VMware的SQL Server虚拟机

虚拟化变得越来越常见,并且在不了解虚拟化如何工作的情况下,DBA在尝试解决性能问题时会出现盲点,例如减少资源争用或改进备份和恢复操作等. 在本文中我们将主要讲述为您的SQL Server工作负载构建理想的基于VMware的虚拟机.我们的下一篇文章将介绍怎么样在Hyper-V上构建对应的SQL Server虚拟化环境. 现在,作为DBA,您可能没有访问权限来创建用于SQL Server的新虚拟机.这些操作可以交给您的VM管理员,他们将为您部署合适的VM环境. 以下详细信息适用于在Windows S

基于I/O的Server/Client实现

在前面的文章中讲了基于NIO实现的Server/Client.本文就讲讲基于同步堵塞式I/O实现的Server/Client好与前面的NIO中的Server/Client进行对照. 网络编程中须要解决的两个主要问题: 1.怎样准确的定位网络上的一台或多台主机. 2.找到主机后怎样可靠高效的进行传输数据. 而解决这两个问题的主要方式就是非常好的运用TCP/IP协议.所以我们所做的网络编程都是基于TCP/IP来实现的. 基于Socket的java网络编程的通信过程: server:使用ServerS

基于群集的Hyper-v Server副本

基于群集的Hyper-v Server副本 前面的博文中和大家聊了Hyper-v副本,相信大家对Hyper-v副本已经有了一个大致了解!今天我们就来给大家介绍一下基于群集的Hyper-v Replica. 前面我们说过Windows Server 2012中,Hyper-v角色引入了Hyper-v副本作为虚拟机内部复制的机制.Hyper-v以异步同步的方式将主站点的虚拟机复制到辅助站点的副本虚拟机. 使用Hyper-v副本可以实现以下优势: 1.恢复能力 - 通过在不同地理位置的分支机构或区域站

【转】libevent和基于libevent的网络编程

转自: http://www.cnblogs.com/nearmeng/p/4043548.html 1 libevent介绍和安装 介绍 libevent是一个轻量级的基于事件驱动的高性能的开源网络库,并且支持多个平台,对多个平台的I/O复用技术进行了封装,当我们编译库的代码时,编译的脚本将会根据OS支持的处理事件机制,来编译相应的代码,从而在libevent接口上保持一致. 在当前的服务器上,面对的主要问题就是要能处理大量的连接.而通过libevent这个网络库,我们就可以调用它的API来很

定制Asp.NET 5 MVC内建身份验证机制 - 基于自建SQL Server用户/角色数据表的表单身份验证

背景 在需要进行表单认证的Asp.NET 5 MVC项目被创建后,往往需要根据项目的实际需求做一系列的工作对MVC 5内建的身份验证机制(Asp.NET Identity)进行扩展和定制: Asp.NET内建的身份验证机制会使用Local DB(本地数据库)读写用户相关的信息,而在数据库驱动的项目中,管理业务信息的数据库通常是特定的数据库环境,比如远程SQL Server数据库实例或Access数据库等等,业务数据库中保存着一系列针对业务需求的数据表,因此需要定制MVC 5内建身份验证,使其操作

基于NIO的Client/Server程序实践

转自    http://blog.csdn.net/zhangzhaokun/article/details/6612833 本意是想看明白Zookeeper内部的代码是怎么玩的,在琢磨一段时间之后,发现还是自己先独立写一个基于NIO的C/S模式程序,看看有哪些细微之处要注意,再来跟进ZK的细节比较靠谱一些,于是乎就自己练手写了如下这段代码 ,权当预热下使用NIO来编写网络程序这一知识点了,在这里记述这段代码的目的无非是加深下自己的印象,并且后续还可以有思索和改进的空间. 基本功能:服务器端不

C#从基于FTPS的FTP server下载数据 (FtpWebRequest 的使用)SSL 加密

FTPS,亦或是FTPES, 是FTP协议的一种扩展,用于对TLS和SSL协议的支持. 本文讲述了如何从一个基于FTPS的Server中下载数据的实例. 任何地方,如有纰漏,欢迎诸位道友指教. 话不多,上码. 1 using System; 2 using System.Net; 3 using System.IO; 4 using System.Net.Security; 5 using System.Security.Cryptography.X509Certificates; 6 7 na