参考这篇文章:
http://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/
这里面使用的是 libev ,不是libevent
Nodejs就是采用libev作为底层库。
先要进行安装,找到了这篇文章:
http://www.cnblogs.com/wunaozai/p/3950249.html
搜索了很多关于如何学网络编程的博客和问答。大致都是推荐学一个网络库,至于C++网络库有那么几个,各有各的好处。
这里就选这个代码量少了,方便入门,等有一定的基础后,再看看“学之者生,用之着死”的ace或者有可能成为C++标准网络库的boost::asio,这个都是后话了。
libev原地址国外的,访问不了。从csdn上下载了一份(link),要1分的,不过我下载过了,再下载就不用了。
拷贝到03机器上,解压。
因为libev是C写的,估计用C++的编译器也没啥用。所以看看默认的编译器行不行。
./configure --prefix=/home/work/data/installed/libev make make install 能够看到安装信息: ---------------------------------------------------------------------- Libraries have been installed in: /home/work/data/installed/libev/lib If you ever happen to want to link against installed libraries in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use the `-LLIBDIR' flag during linking and do at least one of the following: - add LIBDIR to the `LD_LIBRARY_PATH' environment variable during execution - add LIBDIR to the `LD_RUN_PATH' environment variable during linking - use the `-Wl,-rpath -Wl,LIBDIR' linker flag - have your system administrator add LIBDIR to `/etc/ld.so.conf' See any operating system documentation about shared libraries for more information, such as the ld(1) and ld.so(8) manual pages. ---------------------------------------------------------------------- /bin/mkdir -p '/home/work/data/installed/libev/include' /usr/bin/install -c -m 644 ev.h ev++.h event.h '/home/work/data/installed/libev/include' /bin/mkdir -p '/home/work/data/installed/libev/share/man/man3' /usr/bin/install -c -m 644 ev.3 '/home/work/data/installed/libev/share/man/man3' make[1]: Leaving directory `/home/work/data/installed/libev-4.15'
然后在代码目录/home/work/data/code/libev_demo里写了个示例程序libev_demo.c
#include <stdio.h> #include <ev.h> //ev库头文件,注意这是C头文件,C++是ev++.h //定义一个ev_TYPE 的结构体 ev_io stdin_watcher; //定义一个stdin的观测者 ev_timer timeout_watcher; //定义一个timeout的观测者 //所有的watcher的回调函数都有相似的特点 //当stdin有可读的数据时,将会调用下面这个回调函数 static void stdin_cb(EV_P_ ev_io *w, int revents) { puts("stdin ready"); //每一次调用都必须用对应的停止函数,手动的停止其watcher //应该是表示处理过了 ev_io_stop(EV_A_ w); //这将导致所有嵌套执行的ev_run停止监听 ev_break(EV_A_ EVBREAK_ALL); } //这是一个回调函数,用于定时器回调 static void timeout_cb(EV_P_ ev_timer *w, int revents) { puts("timeout"); //这将导致最早运行的ev_run停止监听 ev_break(EV_A_ EVBREAK_ONE); } int main(int argc, char **args) { //使用一般默认的事件循环 struct ev_loop *loop = EV_DEFAULT; //初始化一个I/O watcher,然后启动它 ev_io_init(&stdin_watcher, stdin_cb, 0, EV_READ); ev_io_start(loop, &stdin_watcher); //初始化一个定时器watcher,然后启动它 //只有一次,没有重复的5.5秒定时 ev_timer_init(&timeout_watcher, timeout_cb, 5.5, 0); ev_timer_start(loop, &timeout_watcher); //这里开始loop,等待时间开始计时 ev_run(loop, 0); return 0; }
然后写个Makefile
CXX=gcc INCPATH= /home/work/data/installed/libev/include DEP_LDFLAGS= -L/home/work/data/installed/libev/lib DEP_LDLIBS= -lev TARGET= libev_demo all : $(TARGET) libev_demo : *.c $(CXX) -o $@ $^ -I$(INCPATH) $(DEP_LDFLAGS) $(DEP_LDLIBS) .PHONY : all clean clean : rm -rf $(TARGET)
然后编译并运行:
$ make gcc -o libev_demo libev_demo.c -I/home/work/data/installed/libev/include -L/home/work/data/installed/libev/lib -lev $ ./libev_demo timeout $ ./libev_demo ls stdin ready $ ls libev_demo libev_demo.c Makefile
运行程序后,在超时或者输入字符时会中断退出。并且字符会继续传递到命令行。
另外,以上是C程序,文件后缀不能是cxx。对于cxx,要用C++的头文件。
下面在相同的目录里面写了一个C++的程序 libev_demo.cxx
#include <ev++.h> #include <iostream> #include <unistd.h> using namespace std; class IOWatcher { public: IOWatcher(int fd, unsigned int events) { m_io.set(fd, events); m_io.set<IOWatcher, &IOWatcher::CallBack>(this); m_io.start(); } void CallBack(ev::io &w, int revents) { cout << "In IOWatcher::CallBack" << endl; w.stop(); } private: ev::io m_io; }; int main() { ev::default_loop loop; IOWatcher my_io(STDIN_FILENO, ev::READ); loop.run(0); return 0; }
然后Makefile也要改一下,包括依赖的文件和所使用的编译器:
#CXX=gcc CXX=/opt/compiler/gcc-4.8.2/bin/g++ INCPATH= /home/work/data/installed/libev/include DEP_LDFLAGS= -L/home/work/data/installed/libev/lib DEP_LDLIBS= -lev -Wl,-rpath -Wl,LIBDIR TARGET= libev_demo all : $(TARGET) #libev_demo : *.c libev_demo : *.cxx $(CXX) -o $@ $^ -I$(INCPATH) $(DEP_LDFLAGS) $(DEP_LDLIBS) .PHONY : all clean clean : rm -rf $(TARGET)
编译并运行:
$ make clean rm -rf libev_demo $ make /opt/compiler/gcc-4.8.2/bin/g++ -o libev_demo libev_demo.cxx -I/home/work/data/installed/libev/include
-L/home/work/data/installed/libev/lib -lev -Wl,-rpath -Wl,LIBDIR $ ./libev_demo ls In IOWatcher::CallBack $ ls libev_demo libev_demo.c libev_demo.cxx Makefile
可以看到,C++的程序也能够正确编译和执行。
用gcc -E选项来编译前面的C程序,可以看到预处理的,带有宏展开的程序。
gcc -E libev_demo.c -I/home/work/data/installed/libev/include > tmp
然后原来的c文件,展开成了2271行的文件。
其中main函数部分:
int main(int argc, char **args) { struct ev_loop *loop = ev_default_loop (0); do { do { ((ev_watcher *)(void *)((&stdin_watcher)))->active = ((ev_watcher *)(void *)((&stdin_watcher)))->pending = 0; ( (ev_watcher *)(void *)(((&stdin_watcher))))->priority = (0); (((&stdin_watcher)))->cb = ((stdin_cb)); } while (0); do { ((&stdin_watcher))->fd = ((0)); ((&stdin_watcher))->events = ((EV_READ)) | EV__IOFDSET; } while (0); } while (0); ev_io_start(loop, &stdin_watcher); do { do { ((ev_watcher *)(void *)((&timeout_watcher)))->active = ((ev_watcher *)(void *)((&timeout_watcher)))->pending = 0; ( (ev_watcher *)(void *)(((&timeout_watcher))))->priority = (0); (((&timeout_watcher)))->cb = ((timeout_cb)); } while (0); do { ((ev_watcher_time *)((&timeout_watcher)))->at = ((5.5)); ((&timeout_watcher))->repeat = ((0)); } while (0); } while (0); ev_timer_start(loop, &timeout_watcher); ev_run(loop, 0); return 0; }
Libev通过一个struct ev_loop结构表示一个事件驱动的框架。
在这个框架里面通过ev_xxx结构,ev_init、ev_xxx_set、ev_xxx_start接口箱这个事件驱动的框架里面注册事件监控器,当相应的事件监控器的事件出现时,便会触发该事件监控器的处理逻辑,去处理该事件。处理完之后,这些监控器进入到下一轮的监控中。
符合一个标准的事件驱动状态的模型。
Libev 除了提供了基本的三大类事件(IO事件、定时器事件、信号事件)外还提供了周期事件、子进程事件、文件状态改变事件等多个事件,这里我们用三大基本事件写一个例子。
#include <stdio.h> #include <signal.h> #include <string.h> #include <sys/unistd.h> #include <ev.h> void io_action(struct ev_loop *main_loop, ev_io *io_w, int e) { int rst; char buf[1024]; memset(buf, 0, sizeof(buf)); puts("In IO action"); read(STDIN_FILENO, buf, sizeof(buf)); buf[1023] = '