在我的昨天的博文《[原]我在Windows环境下的首个Libevent测试实例》中介绍了在Windows环境下如何编译一个echo server例子。今天我又试了一下在Linux环境中编译这个例子,遇到了一些问题,学习到了很多知识。这里也顺便记一下,增强一下理解。直接上代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <sys/socket.h> 3 #include <event2/event.h> 4 #include <event2/bufferevent.h> 5 using namespace std; 6 7 void do_accept(evutil_socket_t listener, short event, void *arg); 8 void read_cb(struct bufferevent *bev, void *arg); 9 void error_cb(struct bufferevent *bev, short event, void *arg); 10 void write_cb(struct bufferevent *bev, void *arg); 11 12 int main() 13 { 14 int ret = 0; 15 evutil_socket_t listener; 16 listener = socket(AF_INET, SOCK_STREAM, 0); 17 evutil_make_listen_socket_reuseable(listener); 18 19 struct sockaddr_in sin; 20 sin.sin_family = AF_INET; 21 sin.sin_addr.s_addr = 0; 22 sin.sin_port = htons(6789); 23 if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) 24 { 25 cout << "bind error" << endl; 26 return -1; 27 } 28 if (listen(listener, 32) < 0) 29 { 30 cout << "listen error" << endl; 31 return -1; 32 } 33 cout << "Listening..." << endl; 34 35 evutil_make_socket_nonblocking(listener); 36 struct event_base* base = event_base_new(); 37 if (NULL == base) 38 { 39 cout << "event_base_new error" << endl; 40 return -1; 41 } 42 43 struct event* listen_event = event_new(base, listener, EV_READ | EV_PERSIST, do_accept, (void*)base); 44 event_add(listen_event, NULL); 45 event_base_dispatch(base); 46 event_base_free(base); 47 48 cout << "Done!" << endl; 49 return 0; 50 } 51 52 void do_accept(evutil_socket_t listener, short event, void *arg) 53 { 54 struct event_base* base = (struct event_base *)arg; 55 struct sockaddr_in sin; 56 socklen_t slen = sizeof sin; 57 evutil_socket_t fd = accept(listener, (struct sockaddr *)&sin, &slen); 58 if (fd < 0) 59 { 60 cout << "accept error" << endl; 61 return; 62 } 63 //if (fd > FD_SETSIZE) 64 //{ 65 // cout << "accept����fd����FD_SETSIZE����" << endl; 66 // return; 67 //} 68 cout << "accept:fd=" << fd << endl; 69 70 struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); 71 bufferevent_setcb(bev, read_cb, NULL, error_cb, arg); 72 bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST); 73 } 74 75 void read_cb(struct bufferevent *bev, void *arg) 76 { 77 #define MAX_LINE 256 78 char szLine[MAX_LINE + 1]; 79 evutil_socket_t fd = bufferevent_getfd(bev); 80 81 int n = 0; 82 while (n = bufferevent_read(bev, szLine, MAX_LINE), n > 0) 83 { 84 szLine[n] = '\0'; 85 cout << "Read Line:" << szLine << endl; 86 bufferevent_write(bev, szLine, n); 87 } 88 } 89 90 void write_cb(struct bufferevent *bev, void *arg) 91 { 92 93 } 94 95 void error_cb(struct bufferevent *bev, short event, void *arg) 96 { 97 evutil_socket_t fd = bufferevent_getfd(bev); 98 cout << "error:fd=" << fd << endl; 99 if (event & BEV_EVENT_TIMEOUT) 100 { 101 cout << "Time out!" << endl; 102 } 103 else if (event & BEV_EVENT_EOF) 104 { 105 cout << "EOF!" << endl; 106 } 107 else if (event & BEV_EVENT_ERROR) 108 { 109 cout << "Error!" << endl; 110 } 111 bufferevent_free(bev); 112 }
这个代码与昨天的博文中的代码基本一致,不同的地方在于:
(1)头文件不一样。Linux环境下的头文件默认都是在/usr/include中包含的,如果*.h文件在子目录中,则需要加上子目录的名字;
(2)结构体不同。Windows环境下声明了SOCKADD_IN、SOCKADD、ADDR_ANY等宏,这在Linux环境下是没有的。
到了编译的时候,由于很久没有接触Linux,所以几乎都不会编译了。直接利用g++编译的时候,出现错误提示说找不到event.h头文件,诧异了一下后我明白了,应该是/usr/include目录中没有相应的头文件。但是我用rpm -qa | grep libevent查看时,却发现系统中已经安装了:
[xiaoku@localhost workspace]$ rpm -qa | grep libevent libevent-2.0.21-3.fc20.x86_64
那为什么没有头文件呢?原来,Fedora中默认只是支持了该链接库,却没有提供利用该函数库进行开发的接口,需要安装devel包才行,我以前还一直纳闷devel包是做什么用的呢。利用yum install libevent*就能安装开发包了,安装完之后,我发现我的/usr/include目录下多了一个event2子目录和event.h头文件。再查看就是这样的:
[xiaoku@localhost workspace]$ rpm -qa | grep libevent libevent-2.0.21-3.fc20.x86_64 libevent-devel-2.0.21-3.fc20.x86_64 [xiaoku@localhost workspace]$ ll /usr/include/ | grep event drwxr-xr-x. 2 root root 4096 9月 23 13:54 event2 -rw-r--r--. 1 root root 2760 8月 22 2013 event.h
至此,libevent的开发环境算是搭建好了,下面开始编译吧:
[xiaoku@localhost workspace]$ g++ main.cpp -o echoServer -levent
注意最后的-levent很重要,表示要链接event静态函数库。如果没有这一句,那么整个链接将会出错,类似于下面的结局:
[xiaoku@localhost workspace]$ g++ main.cpp -o echoServer /tmp/ccFk4bSL.o:在函数‘main’中: main.cpp:(.text+0x2c):对‘evutil_make_listen_socket_reuseable’未定义的引用 main.cpp:(.text+0xec):对‘evutil_make_socket_nonblocking’未定义的引用 main.cpp:(.text+0xf1):对‘event_base_new’未定义的引用 main.cpp:(.text+0x13f):对‘event_new’未定义的引用 main.cpp:(.text+0x154):对‘event_add’未定义的引用 main.cpp:(.text+0x160):对‘event_base_dispatch’未定义的引用 main.cpp:(.text+0x16c):对‘event_base_free’未定义的引用 /tmp/ccFk4bSL.o:在函数‘do_accept(int, short, void*)’中: main.cpp:(.text+0x22e):对‘bufferevent_socket_new’未定义的引用 main.cpp:(.text+0x254):对‘bufferevent_setcb’未定义的引用 main.cpp:(.text+0x265):对‘bufferevent_enable’未定义的引用 /tmp/ccFk4bSL.o:在函数‘read_cb(bufferevent*, void*)’中: main.cpp:(.text+0x28f):对‘bufferevent_getfd’未定义的引用 main.cpp:(.text+0x2f5):对‘bufferevent_write’未定义的引用 main.cpp:(.text+0x313):对‘bufferevent_read’未定义的引用 /tmp/ccFk4bSL.o:在函数‘error_cb(bufferevent*, short, void*)’中: main.cpp:(.text+0x357):对‘bufferevent_getfd’未定义的引用 main.cpp:(.text+0x408):对‘bufferevent_free’未定义的引用 collect2: 错误:ld 返回 1