libevent使用IOCP网络模型的示例

这段时间抽空学习了一下强大的网络库libevent,其使用标准C语言编写,支持Windows、Linux、Mac等等主流操作系统,早期版本不支持Windows的IOCP,最新版本已经添加上了,在网上找了一下资料,发现使用IOCP的libevent示例太少,于是结合网上的资料,自己整理编写了一下libevent使用IOCP的小例子。该示例同时支持IPV4以及IPV6的连接。

#ifdef __cplusplus
extern "C"
{
#endif

//包含所需要的头文件
#include "event2/event.h"
#include "event2/listener.h"
#include "event2/bufferevent.h"
#include "event2/thread.h"
#include "event2/buffer.h"

#ifdef __cplusplus
};
#endif

#ifdef _MSC_VER
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"libevent_core.lib")
#endif

//监听回调函数
void listener_cb(evconnlistener *listener, evutil_socket_t fd,
 struct sockaddr *sock, int socklen, void *arg);  

//从Socket接收消息的回调函数
void socket_read_cb(bufferevent *bev, void *arg);

//从Socket事件的回调函数
void socket_event_cb(bufferevent *bev, short events, void *arg);  

int main()
{  
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
    wVersionRequested = MAKEWORD(2, 2);
    //这里必须初始化网络,不然会创建Socket失败
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        /* Tell the user that we could not find a usable */
        /* Winsock DLL.                                  */
        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }

    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(struct sockaddr_in));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(2000);

    struct sockaddr_in6 sin6;
    memset(&sin6, 0, sizeof(struct sockaddr_in6));
    sin6.sin6_family = AF_INET6;
    sin6.sin6_port = htons(2000);

    //告诉libEvent使用Windows线程
    //这句是必须的,不然会导致event_base_dispatch时一直处于Sleep状态,无法正常工作
    evthread_use_windows_threads();

    struct event_config* cfg = event_config_new();
    event_config_set_flag(cfg,EVENT_BASE_FLAG_STARTUP_IOCP);
    //根据CPU实际数量配置libEvent的CPU数
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    event_config_set_num_cpus_hint(cfg,si.dwNumberOfProcessors);

    event_base *base;
    base = event_base_new_with_config(cfg); 
    event_config_free(cfg);

    // 绑定并监听IPV4端口
    evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base,  
        LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,  
        10, (struct sockaddr*)&sin,  
        sizeof(sin));

    // 绑定并监听IPV6端口
    evconnlistener *listener6 = evconnlistener_new_bind(base, listener_cb, base,  
        LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,  
        10, (struct sockaddr*)&sin6,  
        sizeof(sin6));

    //事件分发处理
    event_base_dispatch(base);  

    evconnlistener_free(listener);
    evconnlistener_free(listener6);
    event_base_free(base);
    WSACleanup();

    return 0;  
}

//一个新客户端连接上服务器了  
//当此函数被调用时,libevent已经帮我们accept了这个客户端。该客户端的
//文件描述符为fd
void listener_cb(evconnlistener *listener, evutil_socket_t fd,  
struct sockaddr *sock, int socklen, void *arg)  
{  
    char Buffer[256];
    sockaddr_in* addr = (sockaddr_in*)sock;
    evutil_inet_ntop(addr->sin_family,&addr->sin_addr,Buffer,sizeof(Buffer));
    printf("accept a client %d,IP:%s\n", fd,Buffer);

    event_base *base = (event_base*)arg;  

    //为这个客户端分配一个bufferevent  
    bufferevent *bev =  bufferevent_socket_new(base, fd,  
        BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE);  

    bufferevent_setcb(bev, socket_read_cb, NULL, socket_event_cb, NULL);  
    bufferevent_enable(bev, EV_READ | EV_PERSIST);  
}

void socket_read_cb(bufferevent *bev, void *arg)  
{  
    char msg[4096];

    size_t len;
    // 这里一行一行的读取
    char* p = evbuffer_readln(bufferevent_get_input(bev),&len,EVBUFFER_EOL_ANY);
    if(p)
    {
        // 如果输入exit或者quit则退出程序
        // 可以使用event_base_loopexit或者event_base_loopbreak
        // 它们的区别是前者会把事件处理完才退出,后者是立即退出
        if(!strcmp(p,"exit"))
            event_base_loopexit(bufferevent_get_base(bev),NULL);
        else if (!strcmp(p,"quit"))
            event_base_loopbreak(bufferevent_get_base(bev));

        printf("recv data:%s\n", p);  

        int n = sprintf_s(msg,"srv recv data:%s\n",p);
        //发送消息给客户端
        bufferevent_write(bev, msg, n );

        // 这里记得把分配的内存释放掉,不然会内存泄漏
        free(p);
    }
}

void socket_event_cb(bufferevent *bev, short events, void *arg)  
{  
    if (events & BEV_EVENT_EOF)  
        printf("connection closed\n");  
    else if (events & BEV_EVENT_ERROR)  
        printf("some other error\n");  

    //这将自动close套接字和free读写缓冲区  
    bufferevent_free(bev);  
}

 

 
posted @ 2016-07-01 15:09  witton  阅读(2186)  评论(0编辑  收藏  举报