libevent在windows下使用步骤详解

Libevent 是一个用C语言编写的、轻量级的开源高性能网络库,主要有以下几个亮点:

(1)事件驱动( event-driven),高性能;

(2)轻量级,专注于网络,不如 ACE 那么臃肿庞大;

(3)源代码相当精炼、易读;

(4)跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;

(5)支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;

(6)支持 I/O,定时器和信号等事件;

(7)注册事件优先级。

Libevent 已经被广泛的应用,作为底层的网络库;比如 memcached、 Vomit、 Nylon、 Netchat等等。

 

组成和功能

libevent包含如下部分:

  • evutil

它抽象了一组辅助功能,并屏蔽了操作系统平台的不同。

  • event 及 event_base

这是libevent的核心,提供了一组不依赖于操作系统平台的基于事件的非阻塞IO操作。

  • bufferevent

这个功能基于libevent的事件驱动,提供了非常方便的IO读写,它也可以有多个后端支持,以充分利用操作系统的功能,例如Windows的IOCP API。

  • evbuffer

这个模块是bufferevent的基础,提供了高效的读写方法。

  • evhttp

一个简单的HTTP client/server实现。

  • evdns

一个简单的DNS client/server实现。

  • evrpc

一个简单的RPC实现。

libevent是一个常用的网络库,下面就看看在windows下面编译测试的过程吧。
 

一 环境

系统:win8.1
编译器:VS2008
官方下载地址:http://libevent.org/
版本:2.0.22-stable
 

二 编译静态库

1 解压
把上面下载到libevent-2.0.22-stable.tar.gz解压,得到libevent-2.0.22-stable文件夹


2 添加宏定义
在libevent-2.0.22-stable文件夹下找到下面三个文件:
event_iocp.c
evthread_win32.c
listener.c
打开并在开头加上宏定义:
#define _WIN32_WINNT 0x0500

因为event_iocp.c里用到<winbase.h>头文件里的函数定义,如InitializeCriticalSectionAndSpinCount,
<windows.h>会包含<winbase.h>,而<winbase.h>这个头文件里这个函数是这样定义的:
#if (_WIN32_WINNT >= 0x0403)
 WINBASEAPI
 BOOL WINAPI
 InitializeCriticalSectionAndSpinCount(
     __out LPCRITICAL_SECTION lpCriticalSection,
     __in  DWORD dwSpinCount
     );

 WINBASEAPI
 DWORD
 WINAPI
 SetCriticalSectionSpinCount(
     __inout LPCRITICAL_SECTION lpCriticalSection,
     __in    DWORD dwSpinCount
     );
 #endif

所以要定义_WIN32_WINNT这个宏,而且值要大于0x0403。
如果没有这个宏或不满足条件,编译器会假定这个函数没有定义,
等到链接时再寻找它,这样这个函数的符号就假定返回一个int,
而显示标准库文件里这个函数不是返回int,所以在链接时就会找不到这个函数符号。

注意:宏一定要定义在#include <windows.h>之前,不然还是没有作用。

3 编译
使用vs的命令行工具,cd到libevent-2.0.22-stable目录,执行脚本makefile.nmake,命令如下:
nmake /f Makefile.nmake
这样就会生成三个静态库:
libevent_core.lib
libevent_extras.lib
libevent.lib
 

三 使用示例

1 新建项目

新建一个控制台“空”项目

2 拷贝文件

2.1 在项目目录下建一个libevent文件夹
2.2 在libevent中新建一个lib文件夹,将上面三个lib文件copy到该目录下。
2.3 在libevent中再新建一个include文件夹,
将libevent-2.0.22-stable\include下的文件和文件夹copy到该目录下,
将libevent-2.0.22-stable\WIN32-Code下的文件和文件夹copy到该目录下,
2个event2目录下的文件合并一起。
 

3 项目配置

VC++目录:
包含目录,添加刚刚新建的include目录
库目录,添加刚刚的lib目录;


C/C++:
代码生成-->运行库:
Debug模式下选:多线程调试 (/MTd),
Release下模式下选:多线程 (/MT)


连接器:
输入->附加依赖项:
ws2_32.lib
wsock32.lib
libevent.lib
libevent_core.lib
libevent_extras.lib
另外两个库ws2_32.lib和wsock32.lib是用来编译Windows网络相关的程序库。
 

4 测试代码

4.1 新建一个main.c文件
4.2 从libevent-2.0.22-stable\sample目录下拷贝time-test.c文件中的代码到main中,代码如下:

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>

#pragma comment (lib,”Advapi32.lib”)

#ifndef WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif

#include "event2/bufferevent.h"
#include "event2/buffer.h"
#include "event2/listener.h"
#include "event2/util.h"
#include "event2/event.h"

#include <WinSock2.h>

static const char MESSAGE[] = "Hello, World!\n";

static const int PORT = 9995;


static void conn_writecb(struct bufferevent *bev, void *user_data)
{
    struct evbuffer *output = bufferevent_get_output(bev);
    if (evbuffer_get_length(output) == 0) 
    {
        printf("flushed answer\n");
        bufferevent_free(bev);
    }
}

static void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
    if (events & BEV_EVENT_EOF) 
    {
        printf("Connection closed.\n");
    } 
    else if (events & BEV_EVENT_ERROR) 
    {
        printf("Got an error on the connection: %s\n",
            strerror(errno));/*XXX win32*/
    }
    /* None of the other events can happen here, since we haven't enabled
     * timeouts */
    bufferevent_free(bev);
}

static void signal_cb(evutil_socket_t sig, short events, void *user_data)
{
    struct event_base *base = (struct event_base *)user_data;
    struct timeval delay = { 2, 0 };

    printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");

    event_base_loopexit(base, &delay);
}

static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data)
{
    struct event_base *base = (struct event_base *)user_data;
    struct bufferevent *bev;

    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if (!bev) 
    {
        fprintf(stderr, "Error constructing bufferevent!");
        event_base_loopbreak(base);
        return;
    }
    bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
    bufferevent_enable(bev, EV_WRITE);
    bufferevent_disable(bev, EV_READ);

    bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}

int main(int argc, char **argv)
{
    struct event_base *base;
    struct evconnlistener *listener;
    struct event *signal_event;

    struct sockaddr_in sin;

#ifdef WIN32
    WSADATA wsa_data;
    WSAStartup(0x0201, &wsa_data);
#endif

    base = event_base_new();
    if (!base) 
    {
        fprintf(stderr, "Could not initialize libevent!\n");
        return 1;
    }

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT);

    listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
        LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
        (struct sockaddr*)&sin,
        sizeof(sin));

    if (!listener) 
    {
        fprintf(stderr, "Could not create a listener!\n");
        return 1;
    }

    signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);

    if (!signal_event || event_add(signal_event, NULL)<0) 
    {
        fprintf(stderr, "Could not create/add a signal event!\n");
        return 1;
    }

    event_base_dispatch(base);

    evconnlistener_free(listener);
    event_free(signal_event);
    event_base_free(base);

    printf("done\n");
    return 0;
}

windows下socket通信需要加载Ws2_32.lib,这个大家都知道,至于为何需要Advapi32.lib,可以参考:LNK2019:无法解析的外部符号_imp_CryptAcquireContextA@20
然后将libevent自带的例子C:\Users\zhang\Desktop\libevent-2.0.22-stable\sample|h\ello-world.c拷贝到工程中并编译。

需要注意的是编译时有两处  C2440:“初始化”:无法从“void *”转换为“event_base *”  的错误,强制转换一下即可。

http server:

#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/http.h>

bool startHttpServer(const char* ip, int port, void (*cb)(struct evhttp_request *, void *), void *arg)
{
    //创建event_base和evhttp
    event_base* base = event_base_new();
    evhttp* http_server = evhttp_new(base);
    if (!http_server) {
        return false;
    }
    //绑定到指定地址上
    int ret = evhttp_bind_socket(http_server, ip, port & 0xFFFF);
    if (ret != 0) {
        return false;
    }
    //设置事件处理函数
    evhttp_set_gencb(http_server, cb, arg);
    //启动事件循环,当有http请求的时候会调用指定的回调
    event_base_dispatch(base);
    evhttp_free(http_server);
    return true;
}
void MyHttpServerHandler(struct evhttp_request* req, void* arg)
{
    //创建要使用的buffer对象
    evbuffer* buf = evbuffer_new();
    if (!buf) {
        return;
    }
    //获取请求的URI
    const char* uri = (char*)evhttp_request_get_uri(req);
    //添加对应的HTTP代码
    evbuffer_add_printf(buf,"Hello,world");

	/*
    evbuffer_add_printf(buf,"<head><title>MyHttpServer</title></head>");
    evbuffer_add_printf(buf, "<body>");
    //根据URI显示不同的页面
    if (strcmp(uri, "/") == 0) {
        evbuffer_add_printf(buf,"<p>Welcome to my http server</p>");
    } else if (strcmp(uri, "/about") == 0) {
        evbuffer_add_printf(buf,"<p>I love C++</p>");
    }
    evbuffer_add_printf(buf, "</body>");
    evbuffer_add_printf(buf,"</html>");
	*/
    //回复给客户端
    evhttp_send_reply(req, HTTP_OK, "OK", buf);
    evbuffer_free(buf);
}
int main(int argc, char** argv)
{
    //Windows 平台套接字库的初始化
#ifdef WIN32
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
    //启动服务在地址 127.0.0.1:9000 上
    startHttpServer("127.0.0.1", 9000, MyHttpServerHandler, NULL);
#ifdef WIN32
    WSACleanup();
#endif
    return 0;
}

四 在线文档:

http://www.wangafu.net/~nickm/libevent-2.0/doxygen/html/

五 源码分析:

《Libevent源码分析》对Libevent这个网络库的一些源代码分析

http://blog.csdn.net/sparkliang/article/category/660506

http://blog.csdn.net/luotuo44/article/category/2435521

libevent介绍及示例 

处理大并发之五

windows下编译及使用libevent

libevent学习之二:Windows7(Win7)下编译libevent

事件注册与循环监听

快速理解libevent内在机制

【libevent】

高性能网络编程(二):上一个10年,著名的C10K并发连接问题

libevent event_set函数与event_add函数解析 [SYM]

 

转载于:https://my.oschina.net/mickelfeng/blog/1511673

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值