[原]我在Windows环境下的首个Libevent测试实例
libevent对Windows环境也有很好的支持,不过初次学习和编译libevent简单实例,总是有一些陌生感的,只有成功编译并测试了一个实例,才会有恍然大悟的感觉。下面将要讲到的一个实例是我从网上抄过来的,原创文章地址为:http://www.felix021.com/blog/read.php?2068,表示感谢!
在给出我的第一个测试成功的例子代码之前,简要介绍一下libevent入门的基本知识。原文中作者有一段话是这样的:
“基本的socket编程是阻塞/同步的,每个操作除非已经完成或者出错才会返回,这样对于每一个请求,要使用一个线程或者单独的进程去处理,系统资源没法支撑大量的请求(所谓c10k problem?),例如内存:默认情况下每个线程需要占用2~8M的栈空间。posix定义了可以使用异步的select系统调用,但是因为其采用了轮询的方式来判断某个fd是否变成active,效率不高[O(n)],连接数一多,也还是撑不住。于是各系统分别提出了基于异步/callback的系统调用,例如Linux的epoll,BSD的kqueue,Windows的IOCP。由于在内核层面做了支持,所以可以用O(1)的效率查找到active的fd。基本上,libevent就是对这些高效IO的封装,提供统一的API,简化开发。”
这段话简单来说就是表达了这样一个意思:各大操作系统在内核层面上支持了异步(回调)形式的Socket编程技术,而libevent就是对这些接口进行了一下统一封装罢了。如果操作系统不作这样的支持呢?那么这一切都是白扯。
libevent基础模型大致分为以下几个步骤:
(1)event_base
每一个线程都有且仅有一个event_base,暂且称之为“事件管理器”吧(我自己随便起的名字),对应着一个struct event_base结构体,负责管理schedule托管给它的一系列事件(即下面要介绍的event)。当一个事件发生时,它负责在适当的时间(不一定是立即)去调用相关的回调函数。当回调函数执行完之后,再返回schedule其他事件。
struct event_base* base = event_base_new();
(2)event 与 event_new函数
event_base内部有一个事件管理循环,阻塞在epoll/kqueue等系统调用上,直至有事件发生为止。event_base中管理的对象就是事件(event),这些事件通过event_new来创建和绑定。
struct event* listen_event = event_new(
base, // 事件管理器对象
listener, // 监听的对象,如socket
EV_READ | EV_PERSIST, // 事件类型及属性
do_accept, // 回调函数
(void*)base); // 传递给回调函数的参数
这里面涉及到一点libevent相关的类型声明,一个是事件类型及属性,包括如下种类:
(a)EV_TIMEOUT: 超时
(b)EV_READ: 只要网络缓冲中还有数据,回调函数就会被触发
(c)EV_WRITE: 只要塞给网络缓冲的数据被写完,回调函数就会被触发
(d)EV_SIGNAL: POSIX信号量,参考manual吧
(e)EV_PERSIST: 不指定这个属性的话,回调函数被触发后事件会被删除
(f)EV_ET: Edge-Trigger边缘触发,参考EPOLL_ET
回调函数的声明原型为:
typedef void(* event_callback_fn)(
evutil_socket_t sockfd, // 关联的句柄\文件描述符
short event_type, // 事件类型
void *arg) // 传递给回调函数的参数
(3)event_add 函数
创建好的事件对象用event_add函数添加到消息循环队列中。
event_add(
listen_event, // 事件对象
NULL); // struct timeval* 类型指针,用于设置超时时间,NULL表示无超时设置
(4)event_base_dispatch 函数
事件准备就绪之后,利用event_base_dispatch 函数启动消息循环。
event_base_dispatch(base); // 事件管理器对象
其实多看两遍就看熟悉了,libevent最简单的模型就是这四步,真的很简单。不过下面的例子还涉及到另一个问题,在网络编程中,当tcp服务器段accept建立了一个新的socket之后,如何准备读操作和写操作呢?我参考的原文中对这个问题给出了比较详细的历史描述。历史上,write和read事件分开管理,各自管理数据缓冲区等内容,操作起来非常的麻烦。在libevent2版本开始,引进了为bufferevent类型的管理器,这个管理器能够同时管理write\read\error事件。下面简要介绍一下使用步骤:
<A>设置SOCKET为非阻塞模式
evutil_make_socket_nonblocking(listener);
<B>创建bufferevent对象
struct bufferevent *bev = bufferevent_socket_new(
base, // 事件管理器
fd, // 关联的句柄\文件描述符
BEV_OPT_CLOSE_ON_FREE); // 参数
<C>设置回调函数
bufferevent_setcb(
bev, // bufferevent对象
read_cb, // 读操作回调函数
NULL, // 写操作回调函数
error_cb, // 错误处理回调函数
arg); // 参数
<D>启用事件管理
bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);
<E>执行回调函数
这里涉及到三个回调函数:write_cb、read_cb和error_cb,其函数原型分别是:
// read_cb和write_cb的原型是
void read_or_write_callback(struct bufferevent *bev, void *arg) // error_cb的原型是
void error_cb(struct bufferevent *bev, short error, void *arg)
好了,基础知识就介绍到这里,下面直接给出成功实例代码(在Win7 + VS2008上亲测):
#include <iostream>
#include <WinSock2.h> // Windows环境下的网络通信
#include "event.h" // 使用libevent函数库
using namespace std; // 加载所有相关的静态链接库,也可以在工程参数中指定
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "libevent.lib")
#pragma comment(lib, "libevent_core.lib")
#pragma comment(lib, "libevent_extras.lib") // 回调函数声明
void do_accept(evutil_socket_t listener, short event, void *arg);
void read_cb(struct bufferevent *bev, void *arg);
void error_cb(struct bufferevent *bev, short event, void *arg);
void write_cb(struct bufferevent *bev, void *arg); int main()
{
// 创建SOCKET
WSAData wsaData;
WSAStartup(MAKEWORD(, ), &wsaData); // Windows环境下网络编程必备
int ret = ;
evutil_socket_t listener;
listener = socket(AF_INET, SOCK_STREAM, );
evutil_make_listen_socket_reuseable(listener); // 通用函数:设置SOCKET复用 // 绑定本地地址
SOCKADDR_IN sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ADDR_ANY;
sin.sin_port = htons();
if (bind(listener, (SOCKADDR *)&sin, sizeof(sin)) < )
{
cout << "bind出错!" << endl;
return -;
}
if (listen(listener, ) < )
{
cout << "listen出错!" << endl;
return -;
}
cout << "正在监听……" << endl; // <A>设置SOCKET无阻塞模式
evutil_make_socket_nonblocking(listener); // (1)创建“事件管理器”
struct event_base* base = event_base_new();
if (NULL == base)
{
cout << "event_base_new出错!" << endl;
return -;
} // (2)创建事件
struct event* listen_event = event_new(base, listener, EV_READ | EV_PERSIST, do_accept, (void*)base); // (3)添加事件
event_add(listen_event, NULL); // (4)启动事件管理循环
event_base_dispatch(base); cout << "Done!" << endl;
return ;
} void do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base* base = (struct event_base *)arg;
SOCKADDR_IN sin;
int slen = sizeof sin;
evutil_socket_t fd = accept(listener, (SOCKADDR *)&sin, &slen);
if (fd < )
{
cout << "accept出错!" << endl;
return;
}
//if (fd > FD_SETSIZE)
//{
// cout << "accept返回fd超出FD_SETSIZE限制" << endl;
// return;
//}
cout << "accept:fd=" << fd << endl; // <B>创建“读写事件管理器”,自libevent2之后才有的
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); // <C>设置“读写事件管理器”的回调函数
bufferevent_setcb(bev, read_cb, NULL, error_cb, arg); // <D>启动“读写事件管理器”
bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);
} void read_cb(struct bufferevent *bev, void *arg)
{
#define MAX_LINE 256
char szLine[MAX_LINE + ];
evutil_socket_t fd = bufferevent_getfd(bev); int n = ;
while (n = bufferevent_read(bev, szLine, MAX_LINE), n > )
{
szLine[n] = '\0';
cout << "Read Line:" << szLine << endl;
bufferevent_write(bev, szLine, n);
}
} void write_cb(struct bufferevent *bev, void *arg)
{ } void error_cb(struct bufferevent *bev, short event, void *arg)
{
evutil_socket_t fd = bufferevent_getfd(bev);
cout << "error:fd=" << fd << endl;
if (event & BEV_EVENT_TIMEOUT)
{
cout << "Time out!" << endl;
}
else if (event & BEV_EVENT_EOF)
{
cout << "EOF!" << endl;
}
else if (event & BEV_EVENT_ERROR)
{
cout << "Error!" << endl;
}
bufferevent_free(bev);
}
[原]我在Windows环境下的首个Libevent测试实例的更多相关文章
- [原]在Fedora中编译Libevent测试实例
在我的昨天的博文<[原]我在Windows环境下的首个Libevent测试实例>中介绍了在Windows环境下如何编译一个echo server例子.今天我又试了一下在Linux环境中编译 ...
- Django框架学习笔记(windows环境下安装)
博主最近开始学习主流框架django 网上大部分的安装环境都linux的 由于博主在windows环境下已经有了 Pycharm编辑器 ,所以决定还是继续在windows环境下学习 首先是下载 链接 ...
- 【大数据系列】windows环境下搭建hadoop开发环境使用api进行基本操作
前言 搭建完hadoop集群之后在windows环境下搭建java项目进行测试 操作hdfs中的文件 版本一 package com.slp.hadoop274.hdfs; import java.i ...
- XAMPP、PHPstorm和PHPcharm和Windows环境下Python搭建+暴力破解
XAMPP的安装和使用 一.什么是XAMPP? XAMPP是最流行的PHP开发环境. XAMPP是完全免费且易于安装的Apache发行版,其中包含Apache.MariaDB.PHP和Perl. 类似 ...
- 关于docker在windows环境下运行的第一次体验
关于docker在windows环境下执行的原理 1.1. 首先是Docker Quickstart启动,如果在虚拟机Oracle VM VirtualBox不存在default虚 ...
- Windows环境下32位汇编语言程序设计(典藏版)
Windows环境下32位汇编语言程序设计(典藏版)(含CD光盘1张)(年,经典再现!) 罗云彬 著 ISBN 978-7-121-20759-4 2013年7月出版 定价:99.00元 756页 1 ...
- windows环境下nutch2.x 在eclipse中实现抓取数据存进mysql详细步骤
nutch2.x 在eclipse中实现抓取数据存进mysql步骤 最近在研究nutch,花了几天时间,也遇到很多问题,最终结果还是成功了,在此记录,并给其他有兴趣的人提供参考,共同进步. 对nutc ...
- 浅谈Windows环境下DOS及MS-DOS以及常见一些命令的介绍
浅谈Windows环境下DOS及MS-DOS以及常见一些命令的介绍 前记 自己是搞编程的,首先我是一个菜鸟,接触计算机这么久了,感觉很多计算机方面的技术和知识朦朦胧胧.模模糊糊,貌似有些贻笑大方了:所 ...
- Windows环境下文件的彻底删除与恢复,推荐几个工具(整理)
1. 背景 在Windows(从XP到Win7)中删除文件时,无论是Delete或者是Shift+Delete,都不能真正的删除文件,它做的事情只是从文件分配表中删除了该文件的信息,而并未将文件从存储 ...
随机推荐
- 多线程编程3 - NSOperationQueue
一.简介 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的.也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步 ...
- LeetCode Search a 2D Matrix II (技巧)
题意: 有一个矩阵,每行有序,每列也有序.判断一个数target是否存在于此矩阵中. 思路: 从右上角开始,如果当前数字<target,则该行作废.若当前数字>target,该列作废.这样 ...
- DNS劫持 DNS污染
编号:1021时间:2016年6月24日17:23:50功能:DNS劫持 DNS污染URL:http://www.itechzero.com/dns-hijacking-dns-pollution-i ...
- js获取非行内样式
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- phpcms v9中调用多栏目的方法--get标签(备实例)
如调用栏目id为1,2,7的栏目列表: {pc:get sql="select * from v9_category where catid IN (1,2,7)"} {loop ...
- jellyfish K-mer analysis and genome size estimate
http://www.cbcb.umd.edu/software/jellyfish/ http://www.genome.umd.edu/jellyfish.html https://githu ...
- 转载:LBP代码详细注释
%LBP returns the local binary pattern image or LBP histogram of an image.% J = LBP(I,R,N,MAPPING,MOD ...
- CrossApp 0.3.1示例编译问题解决过程
1 AlertTest.h找不到 问题成因:HelloCpp工程中头文件搜索路径没有增加Classes目录,需要自己加进去.(另外由于这些文件都是在子目录中,用递归模式也行,逐个子目录添加也行) 2 ...
- CSS基础知识点(一)
CSS(Cascading Style Sheets)全称为:层叠样式表. 1.HTML元素类型 (1) 内联元素(inline):可以理解为“文本模式”,即从左到右顺序显示,不单独换行.常用的内联元 ...
- Web上下文配置【MvcConfig】
基于Servlet3.0规范和SpringMVC4注解式配置方式,实现零xml配置,弄了个小demo,供交流讨论. 项目说明如下: 1.db.sql是项目中用到的表,数据库使用的是oracle11g ...