├── .gitignore ├── 101_helloworld_fu_wu_qi.md ├── 102_hui_xian_fu_wu_qi.md ├── 102_ji_yu_shi_jian_fu_wu_qi.md ├── 103_libeventshi_xian_http_fu_wu_qi.md ├── 10_ji_yu_libevent_fu_wu_qi.md ├── 2-epoll.md ├── 21-流-io.md ├── 21-解决阻塞死等待的办法.md ├── 23-什么是epoll.md ├── 24-epollapi.md ├── 25hong_fa_mo_5f0f_md.md ├── 26-简单的epoll服务器.md ├── 3-epoll和reactor.md ├── 31_reactorfan_ying_dui_mo_shi.md ├── 32_epollde_fan_ying_dui_mo_shi_shi_xian.md ├── 4-libevent简单服务器.md ├── 41_jian_li_mo_ren_de_event_base.md ├── 41_ri_zhi_xiao_xi_hui_diao_she_zhi.md ├── 42_jian_cha_event_base_hou_duan.md ├── 42_zhi_ming_cuo_wu_hui_diao_she_zhi.md ├── 43_nei_cun_guan_li_hui_diao_she_zhi.md ├── 43_shi_fang_event_base.md ├── 43_suo_he_xian_cheng_de_she_zhi.md ├── 44_eventbase_you_xian_ji.md ├── 45_diao_shi_shi_jian_de_shi_yong.md ├── 45_eventbase_he_fork.md ├── 5-libevent编程api.md ├── 51_yun_xing_xun_huan.md ├── 52_ting_zhi_xun_huan.md ├── 53_zhuan_chu_event_base_de_zhuang_tai.md ├── 5_eventloop_shi_jian_xun_huan.md ├── 61_chuang_jian_shi_jian.md ├── 62_shi_jian_de_wei_jue_he_fei_wei_jue.md ├── 63_shi_jian_de_you_xian_ji.md ├── 64_jian_cha_shi_jian_zhuang_tai.md ├── 65_yi_ci_hong_fa_shi_jian.md ├── 66_shou_dong_ji_huo_shi_jian.md ├── 67_shi_jian_zhuang_tai_zhi_jian_de_zhuan_huan.md ├── 6_shi_jian.md ├── 71_hui_diao_he_shui_wei.md ├── 72_yan_chi_hui_diao.md ├── 73_bufferevent_xuan_xiang_biao_zhi.md ├── 74_shi_yong_bufferevent.md ├── 751_shi_fang_bufferevent_cao_zuo.md ├── 752_cao_zuo_hui_diao_3001_shui_wei_he_qi_7528_jin_.md ├── 753_cao_zuo_bufferevent_zhong_de_shu_ju.md ├── 755_buffereventde_qing_kong_cao_zuo.md ├── 75_tong_yong_bufferevent_cao_zuo.md ├── 7_bufferevent.md ├── 81_chuang_jian_he_shi_fang_evbuffer.md ├── 81_chuang_jianhe_shi_fang_evconnlistener.md ├── 82_evbufferyu_xian_cheng_an_quan.md ├── 82_qi_yong_he_jin_yong_evconnlistener.md ├── 83_diao_zheng_evconnlistener_de_hui_diao_han_shu.md ├── 83_jian_cha_evbuffer.md ├── 84_jian_ce_evconnlistener.md ├── 84_xiang_evbuffer_tian_jia_shu_ju.md ├── 85_evbuffershu_ju_yi_dong.md ├── 85_zhen_ce_cuo_wu.md ├── 86_tian_jia_shu_ju_dao_evbuffer_qian.md ├── 8_evbuffer.md ├── 8_lian_jie_jian_ting_qi_evconnlistener.md ├── README.md ├── SUMMARY.md ├── assets ├── libevent-2-epoll-api01.png ├── libevent-2-epoll-api02.png ├── libevent-2-epoll-api03.png ├── libevent-2-io操作01.png ├── libevent-2-io操作02.png ├── libevent-2-解决阻塞01.png ├── libevent-2-解决阻塞02.png ├── libevent-2-阻塞01.png ├── libevent-2-阻塞02.png └── libevent深入浅出封面.jpg ├── chapter1.md ├── libevent-2-epoll触发模式01.png ├── libevent-2-epoll触发模式02.png ├── libevent-2-epoll触发模式03.png ├── libevent-2-epoll触发模式04.png ├── libevent-2-解决阻塞03.png ├── libevent-2-解决阻塞04.png ├── libevent-3-反应堆模式.png ├── libevent-3-反应堆模式02.png └── libevent-6-事件状态图.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | _book 12 | 13 | # eBook build output 14 | *.epub 15 | *.mobi 16 | *.pdf -------------------------------------------------------------------------------- /101_helloworld_fu_wu_qi.md: -------------------------------------------------------------------------------- 1 | # 10.1 Hello_World服务器 2 | 3 | ```cpp 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifndef WIN32 9 | #include 10 | # ifdef _XOPEN_SOURCE_EXTENDED 11 | # include 12 | # endif 13 | #include 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | static const char MESSAGE[] = "Hello, World!\n"; 23 | 24 | static const int PORT = 9995; 25 | 26 | static void listener_cb(struct evconnlistener *, evutil_socket_t, 27 | struct sockaddr *, int socklen, void *); 28 | static void conn_writecb(struct bufferevent *, void *); 29 | static void conn_eventcb(struct bufferevent *, short, void *); 30 | static void signal_cb(evutil_socket_t, short, void *); 31 | 32 | int 33 | main(int argc, char **argv) 34 | { 35 | struct event_base *base; 36 | struct evconnlistener *listener; 37 | struct event *signal_event; 38 | 39 | struct sockaddr_in sin; 40 | #ifdef WIN32 41 | WSADATA wsa_data; 42 | WSAStartup(0x0201, &wsa_data); 43 | #endif 44 | 45 | base = event_base_new(); 46 | if (!base) { 47 | fprintf(stderr, "Could not initialize libevent!\n"); 48 | return 1; 49 | } 50 | 51 | memset(&sin, 0, sizeof(sin)); 52 | sin.sin_family = AF_INET; 53 | sin.sin_port = htons(PORT); 54 | 55 | listener = evconnlistener_new_bind(base, listener_cb, (void *)base, 56 | LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1, 57 | (struct sockaddr*)&sin, 58 | sizeof(sin)); 59 | 60 | if (!listener) { 61 | fprintf(stderr, "Could not create a listener!\n"); 62 | return 1; 63 | } 64 | 65 | signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base); 66 | 67 | if (!signal_event || event_add(signal_event, NULL)<0) { 68 | fprintf(stderr, "Could not create/add a signal event!\n"); 69 | return 1; 70 | } 71 | 72 | event_base_dispatch(base); 73 | 74 | evconnlistener_free(listener); 75 | event_free(signal_event); 76 | event_base_free(base); 77 | 78 | printf("done\n"); 79 | return 0; 80 | } 81 | 82 | static void 83 | listener_cb(struct evconnlistener *listener, evutil_socket_t fd, 84 | struct sockaddr *sa, int socklen, void *user_data) 85 | { 86 | struct event_base *base = user_data; 87 | struct bufferevent *bev; 88 | 89 | bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); 90 | if (!bev) { 91 | fprintf(stderr, "Error constructing bufferevent!"); 92 | event_base_loopbreak(base); 93 | return; 94 | } 95 | bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL); 96 | bufferevent_enable(bev, EV_WRITE); 97 | bufferevent_disable(bev, EV_READ); 98 | 99 | bufferevent_write(bev, MESSAGE, strlen(MESSAGE)); 100 | } 101 | 102 | static void 103 | conn_writecb(struct bufferevent *bev, void *user_data) 104 | { 105 | struct evbuffer *output = bufferevent_get_output(bev); 106 | if (evbuffer_get_length(output) == 0) { 107 | printf("flushed answer\n"); 108 | bufferevent_free(bev); 109 | } 110 | } 111 | 112 | static void 113 | conn_eventcb(struct bufferevent *bev, short events, void *user_data) 114 | { 115 | if (events & BEV_EVENT_EOF) { 116 | printf("Connection closed.\n"); 117 | } else if (events & BEV_EVENT_ERROR) { 118 | printf("Got an error on the connection: %s\n", 119 | strerror(errno));/*XXX win32*/ 120 | } 121 | /* None of the other events can happen here, since we haven't enabled 122 | * timeouts */ 123 | bufferevent_free(bev); 124 | } 125 | 126 | static void 127 | signal_cb(evutil_socket_t sig, short events, void *user_data) 128 | { 129 | struct event_base *base = user_data; 130 | struct timeval delay = { 2, 0 }; 131 | 132 | printf("Caught an interrupt signal; exiting cleanly in two seconds.\n"); 133 | 134 | event_base_loopexit(base, &delay); 135 | } 136 | ``` -------------------------------------------------------------------------------- /102_hui_xian_fu_wu_qi.md: -------------------------------------------------------------------------------- 1 | # 10.3 回显服务器 2 | 3 | ```cpp 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | static void 16 | echo_read_cb(struct bufferevent *bev, void *ctx) 17 | { 18 | /* This callback is invoked when there is data to read on bev. */ 19 | struct evbuffer *input = bufferevent_get_input(bev); 20 | struct evbuffer *output = bufferevent_get_output(bev); 21 | 22 | /* Copy all the data from the input buffer to the output buffer. */ 23 | evbuffer_add_buffer(output, input); 24 | } 25 | 26 | static void 27 | echo_event_cb(struct bufferevent *bev, short events, void *ctx) 28 | { 29 | if (events & BEV_EVENT_ERROR) 30 | perror("Error from bufferevent"); 31 | if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { 32 | bufferevent_free(bev); 33 | } 34 | } 35 | 36 | static void 37 | accept_conn_cb(struct evconnlistener *listener, 38 | evutil_socket_t fd, struct sockaddr *address, int socklen, 39 | void *ctx) 40 | { 41 | /* We got a new connection! Set up a bufferevent for it. */ 42 | struct event_base *base = evconnlistener_get_base(listener); 43 | struct bufferevent *bev = bufferevent_socket_new( 44 | base, fd, BEV_OPT_CLOSE_ON_FREE); 45 | 46 | bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL); 47 | 48 | bufferevent_enable(bev, EV_READ|EV_WRITE); 49 | } 50 | 51 | static void 52 | accept_error_cb(struct evconnlistener *listener, void *ctx) 53 | { 54 | struct event_base *base = evconnlistener_get_base(listener); 55 | int err = EVUTIL_SOCKET_ERROR(); 56 | fprintf(stderr, "Got an error %d (%s) on the listener. " 57 | "Shutting down.\n", err, evutil_socket_error_to_string(err)); 58 | 59 | event_base_loopexit(base, NULL); 60 | } 61 | 62 | int 63 | main(int argc, char **argv) 64 | { 65 | struct event_base *base; 66 | struct evconnlistener *listener; 67 | struct sockaddr_in sin; 68 | 69 | int port = 9876; 70 | 71 | if (argc > 1) { 72 | port = atoi(argv[1]); 73 | } 74 | if (port<=0 || port>65535) { 75 | puts("Invalid port"); 76 | return 1; 77 | } 78 | 79 | base = event_base_new(); 80 | if (!base) { 81 | puts("Couldn't open event base"); 82 | return 1; 83 | } 84 | 85 | /* Clear the sockaddr before using it, in case there are extra 86 | * platform-specific fields that can mess us up. */ 87 | memset(&sin, 0, sizeof(sin)); 88 | /* This is an INET address */ 89 | sin.sin_family = AF_INET; 90 | /* Listen on 0.0.0.0 */ 91 | sin.sin_addr.s_addr = htonl(0); 92 | /* Listen on the given port. */ 93 | sin.sin_port = htons(port); 94 | 95 | listener = evconnlistener_new_bind(base, accept_conn_cb, NULL, 96 | LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, 97 | (struct sockaddr*)&sin, sizeof(sin)); 98 | if (!listener) { 99 | perror("Couldn't create listener"); 100 | return 1; 101 | } 102 | evconnlistener_set_error_cb(listener, accept_error_cb); 103 | 104 | event_base_dispatch(base); 105 | return 0; 106 | } 107 | ``` -------------------------------------------------------------------------------- /102_ji_yu_shi_jian_fu_wu_qi.md: -------------------------------------------------------------------------------- 1 | # 10.2 基于事件服务器 2 | ##服务端 3 | ```cpp 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | static void 20 | fifo_read(evutil_socket_t fd, short event, void *arg) 21 | { 22 | char buf[255]; 23 | int len; 24 | struct event *ev = arg; 25 | 26 | /* Reschedule this event */ 27 | event_add(ev, NULL); 28 | 29 | fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n", 30 | (int)fd, event, arg); 31 | len = read(fd, buf, sizeof(buf) - 1); 32 | 33 | if (len == -1) { 34 | perror("read"); 35 | return; 36 | } else if (len == 0) { 37 | fprintf(stderr, "Connection closed\n"); 38 | return; 39 | } 40 | 41 | buf[len] = '\0'; 42 | 43 | fprintf(stdout, "Read: %s\n", buf); 44 | } 45 | 46 | int 47 | main(int argc, char **argv) 48 | { 49 | struct event evfifo; 50 | 51 | struct stat st; 52 | const char *fifo = "event.fifo"; 53 | int socket; 54 | 55 | if (lstat(fifo, &st) == 0) { 56 | if ((st.st_mode & S_IFMT) == S_IFREG) { 57 | errno = EEXIST; 58 | perror("lstat"); 59 | exit(1); 60 | } 61 | } 62 | 63 | unlink(fifo); 64 | if (mkfifo(fifo, 0600) == -1) { 65 | perror("mkfifo"); 66 | exit(1); 67 | } 68 | 69 | /* Linux pipes are broken, we need O_RDWR instead of O_RDONLY */ 70 | socket = open(fifo, O_RDONLY | O_NONBLOCK, 0); 71 | 72 | if (socket == -1) { 73 | perror("open"); 74 | exit(1); 75 | } 76 | 77 | fprintf(stderr, "Write data to %s\n", fifo); 78 | 79 | /* Initalize the event library */ 80 | event_init(); 81 | 82 | /* Initalize one event */ 83 | event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo); 84 | 85 | /* Add it to the active events, without a timeout */ 86 | event_add(&evfifo, NULL); 87 | 88 | event_dispatch(); 89 | 90 | return (0); 91 | } 92 | 93 | ``` 94 | 95 | ##客户端 96 | 97 | ```cpp 98 | #include 99 | #include 100 | #include 101 | #include 102 | #include 103 | #include 104 | 105 | int main(int argc, char *argv[]) 106 | { 107 | int fd = 0; 108 | char *str = "hello libevent!"; 109 | 110 | fd = open("event.fifo", O_RDWR); 111 | if (fd < 0) { 112 | perror("open error"); 113 | exit(1); 114 | } 115 | 116 | while (1) { 117 | write(fd, str, strlen(str)); 118 | sleep(1); 119 | } 120 | 121 | close(fd); 122 | 123 | return 0; 124 | } 125 | ``` -------------------------------------------------------------------------------- /103_libeventshi_xian_http_fu_wu_qi.md: -------------------------------------------------------------------------------- 1 | # 10.3 libevent实现http服务器 2 | 3 | ```cpp 4 | #include 5 | #include 6 | #include //for getopt, fork 7 | #include //for strcat 8 | //for struct evkeyvalq 9 | #include 10 | #include 11 | //for http 12 | //#include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define MYHTTPD_SIGNATURE "myhttpd v 0.0.1" 20 | 21 | //处理模块 22 | void httpd_handler(struct evhttp_request *req, void *arg) { 23 | char output[2048] = "\0"; 24 | char tmp[1024]; 25 | 26 | //获取客户端请求的URI(使用evhttp_request_uri或直接req->uri) 27 | const char *uri; 28 | uri = evhttp_request_uri(req); 29 | sprintf(tmp, "uri=%s\n", uri); 30 | strcat(output, tmp); 31 | 32 | sprintf(tmp, "uri=%s\n", req->uri); 33 | strcat(output, tmp); 34 | //decoded uri 35 | char *decoded_uri; 36 | decoded_uri = evhttp_decode_uri(uri); 37 | sprintf(tmp, "decoded_uri=%s\n", decoded_uri); 38 | strcat(output, tmp); 39 | 40 | //解析URI的参数(即GET方法的参数) 41 | struct evkeyvalq params; 42 | //将URL数据封装成key-value格式,q=value1, s=value2 43 | evhttp_parse_query(decoded_uri, ¶ms); 44 | //得到q所对应的value 45 | sprintf(tmp, "q=%s\n", evhttp_find_header(¶ms, "q")); 46 | strcat(output, tmp); 47 | //得到s所对应的value 48 | sprintf(tmp, "s=%s\n", evhttp_find_header(¶ms, "s")); 49 | strcat(output, tmp); 50 | 51 | free(decoded_uri); 52 | 53 | //获取POST方法的数据 54 | char *post_data = (char *) EVBUFFER_DATA(req->input_buffer); 55 | sprintf(tmp, "post_data=%s\n", post_data); 56 | strcat(output, tmp); 57 | 58 | /* 59 | 具体的:可以根据GET/POST的参数执行相应操作,然后将结果输出 60 | ... 61 | */ 62 | 63 | /* 输出到客户端 */ 64 | 65 | //HTTP header 66 | evhttp_add_header(req->output_headers, "Server", MYHTTPD_SIGNATURE); 67 | evhttp_add_header(req->output_headers, "Content-Type", "text/plain; charset=UTF-8"); 68 | evhttp_add_header(req->output_headers, "Connection", "close"); 69 | //输出的内容 70 | struct evbuffer *buf; 71 | buf = evbuffer_new(); 72 | evbuffer_add_printf(buf, "It works!\n%s\n", output); 73 | evhttp_send_reply(req, HTTP_OK, "OK", buf); 74 | evbuffer_free(buf); 75 | 76 | } 77 | void show_help() { 78 | char *help = "http://localhost:8080\n" 79 | "-l interface to listen on, default is 0.0.0.0\n" 80 | "-p port number to listen on, default is 1984\n" 81 | "-d run as a deamon\n" 82 | "-t timeout for a http request, default is 120 seconds\n" 83 | "-h print this help and exit\n" 84 | "\n"; 85 | fprintf(stderr,"%s",help); 86 | } 87 | //当向进程发出SIGTERM/SIGHUP/SIGINT/SIGQUIT的时候,终止event的事件侦听循环 88 | void signal_handler(int sig) { 89 | switch (sig) { 90 | case SIGTERM: 91 | case SIGHUP: 92 | case SIGQUIT: 93 | case SIGINT: 94 | event_loopbreak(); //终止侦听event_dispatch()的事件侦听循环,执行之后的代码 95 | break; 96 | } 97 | } 98 | 99 | int main(int argc, char *argv[]) { 100 | //自定义信号处理函数 101 | signal(SIGHUP, signal_handler); 102 | signal(SIGTERM, signal_handler); 103 | signal(SIGINT, signal_handler); 104 | signal(SIGQUIT, signal_handler); 105 | 106 | //默认参数 107 | char *httpd_option_listen = "0.0.0.0"; 108 | int httpd_option_port = 8080; 109 | int httpd_option_daemon = 0; 110 | int httpd_option_timeout = 120; //in seconds 111 | 112 | //获取参数 113 | int c; 114 | while ((c = getopt(argc, argv, "l:p:dt:h")) != -1) { 115 | switch (c) { 116 | case 'l' : 117 | httpd_option_listen = optarg; 118 | break; 119 | case 'p' : 120 | httpd_option_port = atoi(optarg); 121 | break; 122 | case 'd' : 123 | httpd_option_daemon = 1; 124 | break; 125 | case 't' : 126 | httpd_option_timeout = atoi(optarg); 127 | break; 128 | case 'h' : 129 | default : 130 | show_help(); 131 | exit(EXIT_SUCCESS); 132 | } 133 | } 134 | 135 | //判断是否设置了-d,以daemon运行 136 | if (httpd_option_daemon) { 137 | pid_t pid; 138 | pid = fork(); 139 | if (pid < 0) { 140 | perror("fork failed"); 141 | exit(EXIT_FAILURE); 142 | } 143 | if (pid > 0) { 144 | //生成子进程成功,退出父进程 145 | exit(EXIT_SUCCESS); 146 | } 147 | } 148 | 149 | /* 使用libevent创建HTTP Server */ 150 | 151 | //初始化event API 152 | event_init(); 153 | 154 | //创建一个http server 155 | struct evhttp *httpd; 156 | httpd = evhttp_start(httpd_option_listen, httpd_option_port); 157 | evhttp_set_timeout(httpd, httpd_option_timeout); 158 | 159 | //指定generic callback 160 | evhttp_set_gencb(httpd, httpd_handler, NULL); 161 | //也可以为特定的URI指定callback 162 | //evhttp_set_cb(httpd, "/", specific_handler, NULL); 163 | 164 | //循环处理events 165 | event_dispatch(); 166 | 167 | evhttp_free(httpd); 168 | return 0; 169 | } 170 | ``` -------------------------------------------------------------------------------- /10_ji_yu_libevent_fu_wu_qi.md: -------------------------------------------------------------------------------- 1 | # 10 基于libevent服务器 2 | 3 | -------------------------------------------------------------------------------- /2-epoll.md: -------------------------------------------------------------------------------- 1 | # 2 EPOLL 2 | 3 | 本章主要简述epoll的基础理论知识。方便理解libevent。 4 | 5 | -------------------------------------------------------------------------------- /21-流-io.md: -------------------------------------------------------------------------------- 1 | ## 2.1 流?I\/O操作\/阻塞 2 | 3 | ### 2.1.1 流 4 | 5 | * 可以进行I\/O操作的内核对象 6 | * 文件、管道、套接字…… 7 | * 流的入口:文件描述符\(fd\) 8 | 9 | ## 2.1.2 I\/O操作 10 | 11 | ![](/assets/libevent-2-io操作01.png) 12 | 13 | --- 14 | 15 | ![](/assets/libevent-2-io操作02.png) 16 | 17 | --- 18 | 19 | 所有对流的读写操作,我们都可以称之为IO操作。 20 | 21 | > ### 那么当一个流中再没有数据,read的时候,或者说在流中已经写满了数据,再write,我们的IO操作就会出现一种现象,就是阻塞现象。 22 | 23 | ## 2.1.2 阻塞 24 | 25 | 26 | 27 | ![](/assets/libevent-2-阻塞01.png) 28 | 29 | --- 30 | 31 | ![](/assets/libevent-2-阻塞02.png) 32 | 33 | --- 34 | 35 | * 阻塞等待 36 | 37 | 空出大脑可以安心睡觉。(不占用CPU宝贵的时间片) 38 | 39 | 40 | * 非阻塞,忙轮询 41 | 42 | 浪费时间,浪费电话费,占用快递员时间(占用CPU,系统资源) 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /21-解决阻塞死等待的办法.md: -------------------------------------------------------------------------------- 1 | ## 2.2 解决阻塞死等待的办法 2 | 3 | ### 2.2.1 阻塞死等待的缺点 4 | 5 | ![](/assets/libevent-2-解决阻塞01.png) 6 | 7 | ### 2.2.2 办法一:非阻塞、忙轮询 8 | 9 | ![](/assets/libevent-2-解决阻塞02.png) 10 | 11 | 12 | ```cpp 13 | while true { 14 | for i in 流[] { 15 | if i has 数据 { 16 | 读 或者 其他处理 17 | } 18 | } 19 | } 20 | ``` 21 | 22 | 23 | ### 2.2.3 办法二:select 24 | 25 | ![](libevent-2-解决阻塞03.png) 26 | 27 | select 代收员 比较懒,她只会告诉你快递到了,但是是谁到的,你需要挨个快递员问一遍。 28 | 29 | 30 | ```cpp 31 | while true { 32 | select(流[]); //阻塞 33 | 34 | for i in 流[] { 35 | if i has 数据 { 36 | 读 或者 其他处理 37 | } 38 | } 39 | } 40 | ``` 41 | ### 2.2.3 办法三:epoll 42 | ![](libevent-2-解决阻塞04.png) 43 | ```cpp 44 | while true { 45 | 可处理的流[] = epoll_wait(epoll_fd); //阻塞 46 | 47 | for i in 可处理的流[] { 48 | 读 或者 其他处理 49 | } 50 | } 51 | 52 | ``` -------------------------------------------------------------------------------- /23-什么是epoll.md: -------------------------------------------------------------------------------- 1 | ## 2.3 什么是epoll 2 | 3 | 4 | 5 | * ### 与select,poll一样,对I\/O多路复用的技术 6 | 7 | * ### 只关心“活跃”的链接,无需遍历全部描述符集合 8 | 9 | * ### 能够处理大量的链接请求\(系统可以打开的文件数目\) 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /24-epollapi.md: -------------------------------------------------------------------------------- 1 | ## 2.4 epoll API 2 | 3 | 2.4.1 创建EPOLL 4 | 5 | ```cpp 6 | /** 7 | * @param size 告诉内核监听的数目 8 | * 9 | * @returns 返回一个epoll句柄(即一个文件描述符) 10 | */ 11 | int epoll_create(int size); 12 | ``` 13 | 14 | ```cpp 15 | int epfd = epoll_create(1000); 16 | ``` 17 | 18 | ![](/assets/libevent-2-epoll-api01.png) 19 | 20 | 2.4.2 控制EPOLL 21 | 22 | ```cpp 23 | /** 24 | * @param epfd 用epoll_create所创建的epoll句柄 25 | * @param op 表示对epoll监控描述符控制的动作 26 | * 27 | * EPOLL_CTL_ADD(注册新的fd到epfd) 28 | * EPOLL_CTL_MOD(修改已经注册的fd的监听事件) 29 | * EPOLL_CTL_DEL(epfd删除一个fd) 30 | * 31 | * @param fd 需要监听的文件描述符 32 | * @param event 告诉内核需要监听的事件 33 | * 34 | * @returns 成功返回0,失败返回-1, errno查看错误信息 35 | */ 36 | int epoll_ctl(int epfd, int op, int fd, 37 | struct epoll_event *event); 38 | 39 | 40 | struct epoll_event { 41 | __uint32_t events; /* epoll 事件 */ 42 | epoll_data_t data; /* 用户传递的数据 */ 43 | } 44 | 45 | /* 46 | * events : {EPOLLIN, EPOLLOUT, EPOLLPRI, 47 | EPOLLHUP, EPOLLET, EPOLLONESHOT} 48 | */ 49 | 50 | typedef union epoll_data { 51 | void *ptr; 52 | int fd; 53 | uint32_t u32; 54 | uint64_t u64; 55 | } epoll_data_t; 56 | 57 | ``` 58 | 59 | ```cpp 60 | struct epoll_event new_event; 61 | 62 | new_event.events = EPOLLIN | EPOLLOUT; 63 | new_event.data.fd = 5; 64 | 65 | epoll_ctl(epfd, EPOLL_CTL_ADD, 5, &new_event); 66 | 67 | ``` 68 | 69 | ![](/assets/libevent-2-epoll-api02.png) 70 | 71 | 2.4.3 等待EPOLL 72 | 73 | ```cpp 74 | 75 | /** 76 | * 77 | * @param epfd 用epoll_create所创建的epoll句柄 78 | * @param event 从内核得到的事件集合 79 | * @param maxevents 告知内核这个events有多大, 80 | * 注意: 值 不能大于创建epoll_create()时的size. 81 | * @param timeout 超时时间 82 | * -1: 永久阻塞 83 | * 0: 立即返回,非阻塞 84 | * >0: 指定微秒 85 | * 86 | * @returns 成功: 有多少文件描述符就绪,时间到时返回0 87 | * 失败: -1, errno 查看错误 88 | */ 89 | int epoll_wait(int epfd, struct epoll_event *event, 90 | int maxevents, int timeout); 91 | 92 | ``` 93 | 94 | 95 | ```cpp 96 | struct epoll_event my_event[1000]; 97 | 98 | int event_cnt = epoll_wait(epfd, my_event, 1000, -1); 99 | 100 | ``` 101 | 102 | 103 | ![](/assets/libevent-2-epoll-api03.png) 104 | 105 | 106 | 107 | 108 | 2.4.4 epoll编程框架 109 | 110 | ```cpp 111 | //创建 epoll 112 | int epfd = epoll_crete(1000); 113 | 114 | //将 listen_fd 添加进 epoll 中 115 | epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd,&listen_event); 116 | 117 | while (1) { 118 | //阻塞等待 epoll 中 的fd 触发 119 | int active_cnt = epoll_wait(epfd, events, 1000, -1); 120 | 121 | for (i = 0 ; i < active_cnt; i++) { 122 | if (evnets[i].data.fd == listen_fd) { 123 | //accept. 并且将新accept 的fd 加进epoll中. 124 | } 125 | else if (events[i].events & EPOLLIN) { 126 | //对此fd 进行读操作 127 | } 128 | else if (events[i].events & EPOLLOUT) { 129 | //对此fd 进行写操作 130 | } 131 | } 132 | } 133 | ``` 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /25hong_fa_mo_5f0f_md.md: -------------------------------------------------------------------------------- 1 | # 2.5 触发模式 2 | 3 | 4 | ## 2.5.1 水平触发 5 | 6 | ![](libevent-2-epoll触发模式01.png) 7 | 8 | 9 | 10 | --- 11 | 12 | ![](libevent-2-epoll触发模式02.png) 13 | 14 | 15 | --- 16 | 17 | 水平触发的主要特点是,如果用户在监听epoll事件,当内核有事件的时候,会拷贝给用户态事件,但是如果用户只处理了一次,那么剩下没有处理的会在下一次epoll_wait再次返回该事件。 18 | 19 | 这样如果用户永远不处理这个事件,就导致每次都会有该事件从内核到用户的拷贝,耗费性能,但是水平触发相对安全,最起码事件不会丢掉,除非用户处理完毕。 20 | 21 | ## 2.5.2 边缘触发 22 | 23 | 24 | ![](libevent-2-epoll触发模式03.png) 25 | 26 | 27 | 28 | --- 29 | 30 | ![](libevent-2-epoll触发模式04.png) 31 | 32 | 边缘触发,相对跟水平触发相反,当内核有事件到达, 只会通知用户一次,至于用户处理还是不处理,以后将不会再通知。这样减少了拷贝过程,增加了性能,但是相对来说,如果用户马虎忘记处理,将会产生事件丢的情况。 33 | -------------------------------------------------------------------------------- /26-简单的epoll服务器.md: -------------------------------------------------------------------------------- 1 | ##2.6 epoll服务器 2 | 3 | 4 | ###2.6.1 服务端 5 | 6 | 7 | ```cpp 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | 21 | #define SERVER_PORT (7778) 22 | #define EPOLL_MAX_NUM (2048) 23 | #define BUFFER_MAX_LEN (4096) 24 | 25 | char buffer[BUFFER_MAX_LEN]; 26 | 27 | void str_toupper(char *str) 28 | { 29 | int i; 30 | for (i = 0; i < strlen(str); i ++) { 31 | str[i] = toupper(str[i]); 32 | } 33 | } 34 | 35 | int main(int argc, char **argv) 36 | { 37 | int listen_fd = 0; 38 | int client_fd = 0; 39 | struct sockaddr_in server_addr; 40 | struct sockaddr_in client_addr; 41 | socklen_t client_len; 42 | 43 | int epfd = 0; 44 | struct epoll_event event, *my_events; 45 | 46 | // socket 47 | listen_fd = socket(AF_INET, SOCK_STREAM, 0); 48 | 49 | // bind 50 | server_addr.sin_family = AF_INET; 51 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 52 | server_addr.sin_port = htons(SERVER_PORT); 53 | bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); 54 | 55 | // listen 56 | listen(listen_fd, 10); 57 | 58 | // epoll create 59 | epfd = epoll_create(EPOLL_MAX_NUM); 60 | if (epfd < 0) { 61 | perror("epoll create"); 62 | goto END; 63 | } 64 | 65 | // listen_fd -> epoll 66 | event.events = EPOLLIN; 67 | event.data.fd = listen_fd; 68 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &event) < 0) { 69 | perror("epoll ctl add listen_fd "); 70 | goto END; 71 | } 72 | 73 | my_events = (epoll_event *)malloc(sizeof(struct epoll_event) * EPOLL_MAX_NUM); 74 | 75 | 76 | while (1) { 77 | // epoll wait 78 | int active_fds_cnt = epoll_wait(epfd, my_events, EPOLL_MAX_NUM, -1); 79 | int i = 0; 80 | for (i = 0; i < active_fds_cnt; i++) { 81 | // if fd == listen_fd 82 | if (my_events[i].data.fd == listen_fd) { 83 | //accept 84 | client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_len); 85 | if (client_fd < 0) { 86 | perror("accept"); 87 | continue; 88 | } 89 | 90 | char ip[20]; 91 | printf("new connection[%s:%d]\n", inet_ntop(AF_INET, &client_addr.sin_addr, ip, sizeof(ip)), ntohs(client_addr.sin_port)); 92 | 93 | event.events = EPOLLIN | EPOLLET; 94 | event.data.fd = client_fd; 95 | epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event); 96 | } 97 | else if (my_events[i].events & EPOLLIN) { 98 | printf("EPOLLIN\n"); 99 | client_fd = my_events[i].data.fd; 100 | 101 | // do read 102 | 103 | buffer[0] = '\0'; 104 | int n = read(client_fd, buffer, 5); 105 | if (n < 0) { 106 | perror("read"); 107 | continue; 108 | } 109 | else if (n == 0) { 110 | epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, &event); 111 | close(client_fd); 112 | } 113 | else { 114 | printf("[read]: %s\n", buffer); 115 | buffer[n] = '\0'; 116 | #if 1 117 | str_toupper(buffer); 118 | write(client_fd, buffer, strlen(buffer)); 119 | printf("[write]: %s\n", buffer); 120 | memset(buffer, 0, BUFFER_MAX_LEN); 121 | #endif 122 | 123 | /* 124 | event.events = EPOLLOUT; 125 | event.data.fd = client_fd; 126 | epoll_ctl(epfd, EPOLL_CTL_MOD, client_fd, &event); 127 | */ 128 | } 129 | } 130 | else if (my_events[i].events & EPOLLOUT) { 131 | printf("EPOLLOUT\n"); 132 | /* 133 | client_fd = my_events[i].data.fd; 134 | str_toupper(buffer); 135 | write(client_fd, buffer, strlen(buffer)); 136 | printf("[write]: %s\n", buffer); 137 | memset(buffer, 0, BUFFER_MAX_LEN); 138 | 139 | event.events = EPOLLIN; 140 | event.data.fd = client_fd; 141 | epoll_ctl(epfd, EPOLL_CTL_MOD, client_fd, &event); 142 | */ 143 | } 144 | } 145 | } 146 | 147 | 148 | 149 | END: 150 | close(epfd); 151 | close(listen_fd); 152 | return 0; 153 | } 154 | 155 | ``` 156 | 157 | 158 | 159 | ###2.6.2 客户端 160 | 161 | 162 | ```cpp 163 | #include 164 | #include 165 | #include 166 | #include 167 | 168 | #include 169 | #include 170 | #include 171 | #include 172 | #include 173 | 174 | #define MAX_LINE (1024) 175 | #define SERVER_PORT (7778) 176 | 177 | void setnoblocking(int fd) 178 | { 179 | int opts = 0; 180 | opts = fcntl(fd, F_GETFL); 181 | opts = opts | O_NONBLOCK; 182 | fcntl(fd, F_SETFL); 183 | } 184 | 185 | int main(int argc, char **argv) 186 | { 187 | int sockfd; 188 | char recvline[MAX_LINE + 1] = {0}; 189 | 190 | struct sockaddr_in server_addr; 191 | 192 | if (argc != 2) { 193 | fprintf(stderr, "usage ./client \n"); 194 | exit(0); 195 | } 196 | 197 | 198 | // 创建socket 199 | if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 200 | fprintf(stderr, "socket error"); 201 | exit(0); 202 | } 203 | 204 | 205 | // server addr 赋值 206 | bzero(&server_addr, sizeof(server_addr)); 207 | server_addr.sin_family = AF_INET; 208 | server_addr.sin_port = htons(SERVER_PORT); 209 | 210 | if (inet_pton(AF_INET, argv[1], &server_addr.sin_addr) <= 0) { 211 | fprintf(stderr, "inet_pton error for %s", argv[1]); 212 | exit(0); 213 | } 214 | 215 | 216 | // 链接服务端 217 | if (connect(sockfd, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0) { 218 | perror("connect"); 219 | fprintf(stderr, "connect error\n"); 220 | exit(0); 221 | } 222 | 223 | setnoblocking(sockfd); 224 | 225 | char input[100]; 226 | int n = 0; 227 | int count = 0; 228 | 229 | 230 | 231 | // 不断的从标准输入字符串 232 | while (fgets(input, 100, stdin) != NULL) 233 | { 234 | printf("[send] %s\n", input); 235 | n = 0; 236 | // 把输入的字符串发送 到 服务器中去 237 | n = send(sockfd, input, strlen(input), 0); 238 | if (n < 0) { 239 | perror("send"); 240 | } 241 | 242 | n = 0; 243 | count = 0; 244 | 245 | 246 | // 读取 服务器返回的数据 247 | while (1) 248 | { 249 | n = read(sockfd, recvline + count, MAX_LINE); 250 | if (n == MAX_LINE) 251 | { 252 | count += n; 253 | continue; 254 | } 255 | else if (n < 0){ 256 | perror("recv"); 257 | break; 258 | } 259 | else { 260 | count += n; 261 | recvline[count] = '\0'; 262 | printf("[recv] %s\n", recvline); 263 | break; 264 | } 265 | } 266 | } 267 | 268 | return 0; 269 | } 270 | 271 | ``` 272 | -------------------------------------------------------------------------------- /3-epoll和reactor.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/3-epoll和reactor.md -------------------------------------------------------------------------------- /31_reactorfan_ying_dui_mo_shi.md: -------------------------------------------------------------------------------- 1 | # 3.1 reactor反应堆模式 2 | 3 | 对每一个构架模式的分析,我们都使用参考文献的分析风格,着重分析意图、上下文、问题、解决方案、结构和实现 6个方面的内容。 4 | 5 | ##1. 意图 6 | 7 | 在事件驱动的应用中,将一个或多个客户的服务请求分离(demultiplex)和调度(dispatch)给应用程序。 8 | 9 | ##2. 上下文 10 | 11 | 在事件驱动的应用中,同步地、有序地处理同时接收的多个服务请求。 12 | 13 | ##3. 问题 14 | 15 | 在分布式系统尤其是服务器这一类事件驱动应用中,虽然这些请求最终会被序列化地处理,但是必须时刻准备着处理多个同时到来的服务请求。在实际应用 中,这些请求总是通过一个事件(如CONNECTOR、READ、WRITE等)来表示的。在有序地处理这些服务请求之前,应用程序必须先分离和调度这些 同时到达的事件。为了有效地解决这个问题,我们需要做到以下4方面: 16 | 17 | 为了提高系统的可测量性和反应时间,应用程序不能长时间阻塞在某个事件源上而停止对其他事件的处理,这样会严重降低对客户端的响应度。 18 | 为了提高吞吐量,任何没有必要的上下文切换、同步和CPU之间的数据移动都要避免。 19 | 引进新的服务或改良已有的服务都要对既有的事件分离和调度机制带来尽可能小的影响。 20 | 大量的应用程序代码需要隐藏在复杂的多线程和同步机制之后。 21 | 22 | ##4. 解决方案 23 | 24 | 在一个或多个事件源上等待事件的到来,例如,一个已经连接的Socket描述符就是一个事件源。将事件的分离和调度整合到处理它的服务中,而将分离和调度机制从应用程序对特定事件的处理中分离开,也就是说分离和调度机制与特定的应用程序无关。 25 | 26 | 具体来说,每个应用程序提供的每个服务都有一个独立的事件处理器与之对应。由事件处理器处理来自事件源的特定类型的事件。每个事件处理器都事先注册 到Reactor管理器中。Reactor管理器使用同步事件分离器在一个或多个事件源中等待事件的发生。当事件发生后,同步事件分离器通知 Reactor管理器,最后由Reactor管理器调度和该事件相关的事件处理器来完成请求的服务。 27 | 28 | ##5. 结构 29 | 30 | 在Reactor模式中,有5个关键的参与者。 31 | 32 | 描述符(handle):由操作系统提供,用于识别每一个事件,如Socket描述符、文件描述符等。在Linux中,它用一个整数来表示。事件可以来自外部,如来自客户端的连接请求、数据等。事件也可以来自内部,如定时器事件。 33 | 同步事件分离器(demultiplexer):是一个函数,用来等待一个或多个事件的发生。调用者会被阻塞,直到分离器分离的描述符集上有事件发生。Linux的select函数是一个经常被使用的分离器。 34 | 事件处理器接口(event handler):是由一个或多个模板函数组成的接口。这些模板函数描述了和应用程序相关的对某个事件的操作。 35 | 具体的事件处理器:是事件处理器接口的实现。它实现了应用程序提供的某个服务。每个具体的事件处理器总和一个描述符相关。它使用描述符来识别事件、识别应用程序提供的服务。 36 | Reactor 管理器(reactor):定义了一些接口,用于应用程序控制事件调度,以及应用程序注册、删除事件处理器和相关的描述符。它是事件处理器的调度核心。 Reactor管理器使用同步事件分离器来等待事件的发生。一旦事件发生,Reactor管理器先是分离每个事件,然后调度事件处理器,最后调用相关的模 板函数来处理这个事件。 37 | 通过上述分析,我们注意到,是Reactor管理器而不是应用程序负责等待事件、分离事件和调度事件。实际上,Reactor管理器并没有被具体的 事件处理器调用,而是管理器调度具体的事件处理器,由事件处理器对发生的事件做出处理。这就是类似Hollywood原则的“反向控制”。应用程序要做的 仅仅是实现一个具体的事件处理器,然后把它注册到Reactor管理器中。接下来的工作由管理器来完成。这些参与者的相互关系如图2-1所示。 38 | 39 | 现在结合第1章分析的框架五元素来看一下Reactor构架模式的参与者与框架五元素之间的关系:Reactor构架模式的具体实现对应了元素1; 事件处理器接口对应元素2;具体的事件处理器对应元素3;Reactor管理器使用了Hollywood原则,可以认为和元素5对应;元素4的功能相对不 明显,没有明确的对应关系。 40 | 41 | 如果还是没有理解Reactor构架模式,没有关系,源代码会说明所有问题。此时可再分析一遍Reactor构架模式,然后继续以下内容。 42 | 43 | 44 | ![](libevent-3-反应堆模式.png) 45 | 46 | 47 | 48 | 49 | ![](libevent-3-反应堆模式02.png) -------------------------------------------------------------------------------- /32_epollde_fan_ying_dui_mo_shi_shi_xian.md: -------------------------------------------------------------------------------- 1 | # 3.2 epoll的反应堆模式实现 2 | 3 | >epoll反应堆模式的实现-也就是libevent的实现原理。 4 | 5 | 6 | ```go 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #define MAX_EVENTS 1024 19 | #define BUFLEN 128 20 | #define SERV_PORT 8080 21 | 22 | 23 | /* 24 | * status:1表示在监听事件中,0表示不在 25 | * last_active:记录最后一次响应时间,做超时处理 26 | */ 27 | struct myevent_s { 28 | int fd; //cfd listenfd 29 | int events; //EPOLLIN EPLLOUT 30 | void *arg; //指向自己结构体指针 31 | void (*call_back)(int fd, int events, void *arg); 32 | int status; 33 | char buf[BUFLEN]; 34 | int len; 35 | long last_active; 36 | }; 37 | 38 | int g_efd; /* epoll_create返回的句柄 */ 39 | struct myevent_s g_events[MAX_EVENTS+1]; /* +1 最后一个用于 listen fd */ 40 | 41 | void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg) 42 | { 43 | ev->fd = fd; 44 | ev->call_back = call_back; 45 | ev->events = 0; 46 | ev->arg = arg; 47 | ev->status = 0; 48 | //memset(ev->buf, 0, sizeof(ev->buf)); 49 | //ev->len = 0; 50 | ev->last_active = time(NULL); 51 | 52 | return; 53 | } 54 | 55 | void recvdata(int fd, int events, void *arg); 56 | void senddata(int fd, int events, void *arg); 57 | 58 | void eventadd(int efd, int events, struct myevent_s *ev) 59 | { 60 | struct epoll_event epv = {0, {0}}; 61 | int op; 62 | epv.data.ptr = ev; 63 | epv.events = ev->events = events; 64 | 65 | if (ev->status == 1) { 66 | op = EPOLL_CTL_MOD; 67 | } 68 | else { 69 | op = EPOLL_CTL_ADD; 70 | ev->status = 1; 71 | } 72 | 73 | if (epoll_ctl(efd, op, ev->fd, &epv) < 0) 74 | printf("event add failed [fd=%d], events[%d]\n", ev->fd, events); 75 | else 76 | printf("event add OK [fd=%d], op=%d, events[%0X]\n", ev->fd, op, events); 77 | 78 | return; 79 | } 80 | 81 | void eventdel(int efd, struct myevent_s *ev) 82 | { 83 | struct epoll_event epv = {0, {0}}; 84 | 85 | if (ev->status != 1) 86 | return; 87 | 88 | epv.data.ptr = ev; 89 | ev->status = 0; 90 | epoll_ctl(efd, EPOLL_CTL_DEL, ev->fd, &epv); 91 | 92 | return; 93 | } 94 | 95 | 96 | void acceptconn(int lfd, int events, void *arg) 97 | { 98 | struct sockaddr_in cin; 99 | socklen_t len = sizeof(cin); 100 | int cfd, i; 101 | 102 | if ((cfd = accept(lfd, (struct sockaddr *)&cin, &len)) == -1) { 103 | if (errno != EAGAIN && errno != EINTR) { 104 | /* 暂时不做出错处理 */ 105 | } 106 | printf("%s: accept, %s\n", __func__, strerror(errno)); 107 | return; 108 | } 109 | 110 | do { 111 | for (i = 0; i < MAX_EVENTS; i++) { 112 | if (g_events[i].status == 0) 113 | break; 114 | } 115 | 116 | if (i == MAX_EVENTS) { 117 | printf("%s: max connect limit[%d]\n", __func__, MAX_EVENTS); 118 | break; 119 | } 120 | 121 | int flag = 0; 122 | if ((flag = fcntl(cfd, F_SETFL, O_NONBLOCK)) < 0) 123 | { 124 | printf("%s: fcntl nonblocking failed, %s\n", __func__, strerror(errno)); 125 | break; 126 | } 127 | 128 | eventset(&g_events[i], cfd, recvdata, &g_events[i]); 129 | eventadd(g_efd, EPOLLIN, &g_events[i]); 130 | } while(0); 131 | 132 | printf("new connect [%s:%d][time:%ld], pos[%d]\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), g_events[i].last_active, i); 133 | 134 | return; 135 | } 136 | 137 | void recvdata(int fd, int events, void *arg) 138 | { 139 | struct myevent_s *ev = (struct myevent_s *)arg; 140 | int len; 141 | 142 | len = recv(fd, ev->buf, sizeof(ev->buf), 0); 143 | eventdel(g_efd, ev); 144 | 145 | if (len > 0) { 146 | ev->len = len; 147 | ev->buf[len] = '\0'; 148 | printf("C[%d]:%s\n", fd, ev->buf); 149 | /* 转换为发送事件 */ 150 | eventset(ev, fd, senddata, ev); 151 | eventadd(g_efd, EPOLLOUT, ev); 152 | } 153 | else if (len == 0) { 154 | close(ev->fd); 155 | /* ev-g_events 地址相减得到偏移元素位置 */ 156 | printf("[fd=%d] pos[%d], closed\n", fd, (int)(ev - g_events)); 157 | } 158 | else { 159 | close(ev->fd); 160 | printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno)); 161 | } 162 | 163 | return; 164 | } 165 | 166 | void senddata(int fd, int events, void *arg) 167 | { 168 | struct myevent_s *ev = (struct myevent_s *)arg; 169 | int len; 170 | 171 | len = send(fd, ev->buf, ev->len, 0); 172 | //printf("fd=%d\tev->buf=%s\ttev->len=%d\n", fd, ev->buf, ev->len); 173 | //printf("send len = %d\n", len); 174 | 175 | eventdel(g_efd, ev); 176 | if (len > 0) { 177 | printf("send[fd=%d], [%d]%s\n", fd, len, ev->buf); 178 | eventset(ev, fd, recvdata, ev); 179 | eventadd(g_efd, EPOLLIN, ev); 180 | } 181 | else { 182 | close(ev->fd); 183 | printf("send[fd=%d] error %s\n", fd, strerror(errno)); 184 | } 185 | 186 | return; 187 | } 188 | 189 | void initlistensocket(int efd, short port) 190 | { 191 | int lfd = socket(AF_INET, SOCK_STREAM, 0); 192 | fcntl(lfd, F_SETFL, O_NONBLOCK); 193 | eventset(&g_events[MAX_EVENTS], lfd, acceptconn, &g_events[MAX_EVENTS]); 194 | eventadd(efd, EPOLLIN, &g_events[MAX_EVENTS]); 195 | 196 | struct sockaddr_in sin; 197 | 198 | memset(&sin, 0, sizeof(sin)); 199 | sin.sin_family = AF_INET; 200 | sin.sin_addr.s_addr = INADDR_ANY; 201 | sin.sin_port = htons(port); 202 | 203 | bind(lfd, (struct sockaddr *)&sin, sizeof(sin)); 204 | 205 | listen(lfd, 20); 206 | 207 | return; 208 | } 209 | 210 | int main(int argc, char *argv[]) 211 | { 212 | unsigned short port = SERV_PORT; 213 | 214 | if (argc == 2) 215 | port = atoi(argv[1]); 216 | 217 | g_efd = epoll_create(MAX_EVENTS+1); 218 | 219 | if (g_efd <= 0) 220 | printf("create efd in %s err %s\n", __func__, strerror(errno)); 221 | 222 | initlistensocket(g_efd, port); 223 | 224 | /* 事件循环 */ 225 | struct epoll_event events[MAX_EVENTS+1]; 226 | 227 | printf("server running:port[%d]\n", port); 228 | int checkpos = 0, i; 229 | while (1) { 230 | /* 超时验证,每次测试100个链接,不测试listenfd 当客户端60秒内没有和服务器通信,则关闭此客户端链接 */ 231 | long now = time(NULL); 232 | for (i = 0; i < 100; i++, checkpos++) { 233 | if (checkpos == MAX_EVENTS) 234 | checkpos = 0; 235 | if (g_events[checkpos].status != 1) 236 | continue; 237 | long duration = now - g_events[checkpos].last_active; 238 | if (duration >= 60) { 239 | close(g_events[checkpos].fd); 240 | printf("[fd=%d] timeout\n", g_events[checkpos].fd); 241 | eventdel(g_efd, &g_events[checkpos]); 242 | } 243 | } 244 | /* 等待事件发生 */ 245 | int nfd = epoll_wait(g_efd, events, MAX_EVENTS+1, 1000); 246 | if (nfd < 0) { 247 | printf("epoll_wait error, exit\n"); 248 | break; 249 | } 250 | for (i = 0; i < nfd; i++) { 251 | struct myevent_s *ev = (struct myevent_s *)events[i].data.ptr; 252 | if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) { 253 | ev->call_back(ev->fd, events[i].events, ev->arg); 254 | } 255 | if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) { 256 | ev->call_back(ev->fd, events[i].events, ev->arg); 257 | } 258 | } 259 | } 260 | 261 | /* 退出前释放所有资源 */ 262 | return 0; 263 | } 264 | ``` -------------------------------------------------------------------------------- /4-libevent简单服务器.md: -------------------------------------------------------------------------------- 1 | #9 libevent常用设置 2 | 3 | 4 | libevent 有一些被整个进程共享的、影响整个库的全局设置。 5 | 6 | 7 | 8 | 9 | >### **必须在调用libevent 库的任何其他部分之前修改这些设置,否则,libevent 会进入不一致的状态。** 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 提示: 18 | >本章节部分内容转自《libevent中文手册(中文版)》 -------------------------------------------------------------------------------- /41_jian_li_mo_ren_de_event_base.md: -------------------------------------------------------------------------------- 1 | # 4.1 创建event_base 2 | 3 | 4 | ###4.1.1 创建默认的event_base 5 | 6 | ***event_base_new()***函数分配并且返回一个新的具有默认设置的 event_base。函数会检测环境变量,返回一个到 event_base 的指针。如果发生错误,则返回 NULL。选择各种方法时,函数会选择 OS 支持的最快方法。 7 | 8 | ```cpp 9 | struct event_base *event_base_new(void); 10 | ``` 11 | 12 | >大多数程序使用这个函数就够了。 13 | 14 | event_base_new()函数声明在中,首次出现在 libevent 1.4.3版。 15 | 16 | 17 | ###4.1.2 创建复杂的event_base 18 | 19 | 要对取得什么类型的 event_base 有更多的控制,就需要使用 **event_config**。 20 | 21 | 22 | event_config 是一个容纳 event_base 配置信息的不透明结构体。需要 event_base 时,将 event_config 传递给**event_base_new_with_config ()。** 23 | 24 | ###创建接口 25 | ```cpp 26 | struct event_config *event_config_new(void); 27 | 28 | struct event_base * 29 | event_base_new_with_config(const struct event_config *cfg); 30 | 31 | void event_config_free(struct event_config *cfg); 32 | ``` 33 | 34 | 要使用这些函数分配 event_base,先调用 event_config_new()分配一个 event_config。 然后,对 event_config 调用其它函数,设置所需要的 event_base 特征。最后,调用 event_base_new_with_config()获取新的 event_base。完成工作后,使用 event_config_free ()释放 event_config。 35 | 36 | 37 | 38 | ```cpp 39 | int event_config_avoid_method(struct event_config *cfg, const char *method); 40 | 41 | enum event_method_feature { 42 | EV_FEATURE_ET = 0x01, 43 | EV_FEATURE_O1 = 0x02, 44 | EV_FEATURE_FDS = 0x04, 45 | }; 46 | int event_config_require_features(struct event_config *cfg, 47 | enum event_method_feature feature); 48 | 49 | enum event_base_config_flag { 50 | EVENT_BASE_FLAG_NOLOCK = 0x01, 51 | EVENT_BASE_FLAG_IGNORE_ENV = 0x02, 52 | EVENT_BASE_FLAG_STARTUP_IOCP = 0x04, 53 | EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08, 54 | EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10, 55 | EVENT_BASE_FLAG_PRECISE_TIMER = 0x20 56 | }; 57 | int event_config_set_flag(struct event_config *cfg, 58 | enum event_base_config_flag flag); 59 | ``` 60 | 61 | 调用 event_config_avoid_method ()可以通过名字让 libevent 避免使用特定的可用后端 。 调用 event_config_require_feature ()让 libevent 不使用不能提供所有指定特征的后端。 调用 event_config_set_flag()让 libevent 在创建 event_base 时设置一个或者多个将在下面介绍的运行时标志。 62 | 63 | **event_config_require_features()可识别的特征值有:** 64 | 65 | * EV_FEATURE_ET:要求支持边沿触发的后端 66 | * EV_FEATURE_O1:要求添加、删除单个事件,或者确定哪个事件激活的操作是 O(1)复杂度的后端 67 | * EV_FEATURE_FDS:要求支持任意文件描述符,而不仅仅是套接字的后端 68 | 69 | **event_config_set_flag()可识别的选项值有:** 70 | 71 | * EVENT_BASE_FLAG_NOLOCK :不要为 event_base 分配锁。设置这个选项可以 为 event_base 节省一点用于锁定和解锁的时间,但是让在多个线程中访问 event_base 成为不安全的。 72 | 73 | 74 | * EVENT_BASE_FLAG_IGNORE_ENV :选择使用的后端时,不要检测 EVENT_* 环境 变量。使用这个标志需要三思:这会让用户更难调试你的程序与 libevent 的交互。 75 | * EVENT_BASE_FLAG_STARTUP_IOCP:仅用于 Windows,让 libevent 在启动时就 启用任何必需的 IOCP 分发逻辑,而不是按需启用。 76 | * EVENT_BASE_FLAG_NO_CACHE_TIME :不是在事件循环每次准备执行超时回调时 检测当前时间,而是在每次超时回调后进行检测。注意:这会消耗更多的 CPU时间。 77 | * 78 | * EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST :告诉 libevent ,如果决定使 用 epoll 后端,可以安全地使用更快的基于 changelist 的后端。epoll-changelist 后端可以 在后端的分发函数调用之间,同样的 fd 多次修改其状态的情况下,避免不必要的系统 调用。但是如果传递任何使用 dup()或者其变体克隆的 fd 给 libevent,epoll-changelist 后端会触发一个内核 bug,导致不正确的结果。在不使用 epoll 后端的情况下,这个标 志是没有效果的。也可以通过设置 79 | 80 | * EVENT_EPOLL_USE_CHANGELIST 环境变量来 打开 epoll-changelist 选项。 81 | 82 | 83 | 上述操作 event_config 的函数都在成功时返回0,失败时返回-1。 84 | 85 | >设置 event_config,请求 OS 不能提供的后端是很容易的 。比如说,对于 libevent 2.0.1-alpha, 在 Windows 中是没有 O(1)后端的;在 Linux 中也没有同时提供 EV_FEATURE_FDS 和 EV_FEATURE_O1 特征的后端。如果创建了 libevent 不能满足的配置, event_base_new_with_config ()会返回 NULL。 86 | -------------------------------------------------------------------------------- /41_ri_zhi_xiao_xi_hui_diao_she_zhi.md: -------------------------------------------------------------------------------- 1 | # 9.1 日志消息回调设置 2 | 3 | libevent 可以记录内部错误和警告。如果编译进日志支持,还会记录调试信息。默认配置下 4 | 这些信息被写到stderr。通过提供定制的日志函数可以覆盖默认行为。 5 | 6 | 7 | 8 | ```cpp 9 | #define EVENT_LOG_DEBUG 0 10 | #define EVENT_LOG_MSG 1 11 | #define EVENT_LOG_WARN 2 12 | #define EVENT_LOG_ERR 3 13 | 14 | /* Deprecated; see note at the end of this section */ 15 | #define _EVENT_LOG_DEBUG EVENT_LOG_DEBUG 16 | #define _EVENT_LOG_MSG EVENT_LOG_MSG 17 | #define _EVENT_LOG_WARN EVENT_LOG_WARN 18 | #define _EVENT_LOG_ERR EVENT_LOG_ERR 19 | 20 | 21 | typedef void (*event_log_cb)(int severity, const char *msg); 22 | void event_set_log_callback(event_log_cb cb); 23 | 24 | ``` 25 | 26 | 要覆盖libevent 的日志行为,编写匹配event_log_cb 签名的定制函数,将其作为参数传递 27 | 给event_set_log_callback()。 28 | 29 | 随后libevent 在日志信息的时候,将会把信息传递给你提供的函数。再次调用event_set_log_callback(),传递参数NULL,就可以恢复默认行为。 30 | 31 | 32 | ### 实例 33 | 34 | ```cpp 35 | #include 36 | #include 37 | 38 | static void discard_cb(int severity, const char *msg) 39 | { 40 | /* This callback does nothing. */ 41 | } 42 | 43 | static FILE *logfile = NULL; 44 | static void write_to_file_cb(int severity, const char *msg) 45 | { 46 | const char *s; 47 | if (!logfile) 48 | return; 49 | switch (severity) { 50 | case _EVENT_LOG_DEBUG: s = "debug"; break; 51 | case _EVENT_LOG_MSG: s = "msg"; break; 52 | case _EVENT_LOG_WARN: s = "warn"; break; 53 | case _EVENT_LOG_ERR: s = "error"; break; 54 | default: s = "?"; break; /* never reached */ 55 | } 56 | fprintf(logfile, "[%s] %s\n", s, msg); 57 | } 58 | 59 | /* Turn off all logging from Libevent. */ 60 | void suppress_logging(void) 61 | { 62 | event_set_log_callback(discard_cb); 63 | } 64 | 65 | /* Redirect all Libevent log messages to the C stdio file 'f'. */ 66 | void set_logfile(FILE *f) 67 | { 68 | logfile = f; 69 | event_set_log_callback(write_to_file_cb); 70 | } 71 | 72 | ``` 73 | 74 | 在用户提供的event_log_cb 回调函数中调用libevent 函数是不安全的。 75 | 76 | 比如说,如果试图编写一个使用bufferevent 将警告信息发送给某个套接字的日志回调函数,可能会遇到奇怪 77 | 而难以诊断的bug。未来版本libevent 的某些函数可能会移除这个限制。 78 | 79 | 80 | 这个函数在中声明,在libevent 1.0c 版本中首次出现。 -------------------------------------------------------------------------------- /42_jian_cha_event_base_hou_duan.md: -------------------------------------------------------------------------------- 1 | # 4.2 检查event_base后端 2 | 3 | 有时候需要检查 event_base 支持哪些特征,或者当前使用哪种方法。 4 | 5 | ###接口1 6 | ```cpp 7 | const char **event_get_supported_methods(void); 8 | ``` 9 | 10 | event_get_supported_methods()函数返回一个指针 ,指向 libevent 支持的方法名字数组 。 这个数组的最后一个元素是 NULL。 11 | 12 | ###实例: 13 | 14 | ```cpp 15 | int i; 16 | const char **methods = event_get_supported_methods(); 17 | printf("Starting Libevent %s. Available methods are:\n", 18 | event_get_version()); 19 | for (i=0; methods[i] != NULL; ++i) { 20 | printf(" %s\n", methods[i]); 21 | } 22 | ``` 23 | 24 | >这个函数返回 libevent 被编译以支持的方法列表 。然而 libevent 运行的时候,操作系统可能 不能支持所有方法。比如说,可能 OS X 版本中的 kqueue 的 bug 太多,无法使用。 25 | 26 | 27 | 28 | ###接口2 29 | 30 | ```cpp 31 | const char * 32 | event_base_get_method(const struct event_base *base); 33 | 34 | enum event_method_feature 35 | event_base_get_features(const struct event_base *base); 36 | ``` 37 | 38 | event_base_get_method()返回 event_base 正在使用的方法。 39 | 40 | event_base_get_features ()返回 event_base 支持的特征的比特掩码。 41 | 42 | 43 | ###实例 44 | 45 | ```cpp 46 | struct event_base *base; 47 | enum event_method_feature f; 48 | 49 | base = event_base_new(); 50 | if (!base) { 51 | puts("Couldn't get an event_base!"); 52 | } else { 53 | printf("Using Libevent with backend method %s.", 54 | event_base_get_method(base)); 55 | f = event_base_get_features(base); 56 | if ((f & EV_FEATURE_ET)) 57 | printf(" Edge-triggered events are supported."); 58 | if ((f & EV_FEATURE_O1)) 59 | printf(" O(1) event notification is supported."); 60 | if ((f & EV_FEATURE_FDS)) 61 | printf(" All FD types are supported."); 62 | puts(""); 63 | } 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /42_zhi_ming_cuo_wu_hui_diao_she_zhi.md: -------------------------------------------------------------------------------- 1 | # 9.2 致命错误回调设置 2 | 3 | 4 | libevent 在检测到不可恢复的内部错误时的默认行为是调用exit()或者abort(),退出正在运行的进程。这类错误通常意味着某处有bug:要么在你的代码中,要么在libevent 中。 5 | 6 | 7 | 如果希望更优雅地处理致命错误,可以为libevent 提供在退出时应该调用的函数,覆盖默认 8 | 行为。 9 | 10 | ```cpp 11 | typedef void (*event_fatal_cb)(int err); 12 | void event_set_fatal_callback(event_fatal_cb cb); 13 | ``` 14 | 15 | 16 | 要使用这些函数,首先定义libevent 在遇到致命错误时应该调用的函数,将其传递给 17 | event_set_fatal_callback()。 18 | 19 | 20 | 21 | 随后libevent 在遇到致命错误时将调用你提供的函数。 22 | 你的函数不应该将控制返回到libevent:这样做可能导致不确定的行为。 23 | 24 | 25 | 为了避免崩溃,libevent 还是会退出。你的函数被不应该调用其它libevent 函数。 26 | 这些函数声明在中,在libevent 2.0.3-alpha 版本中首次出现。 -------------------------------------------------------------------------------- /43_nei_cun_guan_li_hui_diao_she_zhi.md: -------------------------------------------------------------------------------- 1 | # 9.3 内存管理回调设置 2 | 3 | 4 | 默认情况下,libevent 使用C 库的内存管理函数在堆上分配内存。 5 | 6 | 通过提供malloc、realloc和free 的替代函数,可以让libevent 使用其他的内存管理器。 7 | 8 | 9 | 希望libevent 使用一个更高效的分配器时;或者希望libevent 使用一个工具分配器,以便检查内存泄漏时,可能需要这样做。 10 | 11 | 12 | 13 | ```java 14 | void event_set_mem_functions(void *(*malloc_fn)(size_t sz), 15 | void *(*realloc_fn)(void *ptr, size_t sz), 16 | void (*free_fn)(void *ptr)); 17 | ``` 18 | 19 | 这里有个替换libevent 分配器函数的示例,它可以计算已经分配的字节数。 20 | 21 | 实际应用中可能 22 | 需要添加锁,以避免运行在多个线程中时发生错误。 23 | 24 | ### 实例 25 | 26 | ```cpp 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | /* This union's purpose is to be as big as the largest of all the 33 | * types it contains. */ 34 | union alignment { 35 | size_t sz; 36 | void *ptr; 37 | double dbl; 38 | }; 39 | /* We need to make sure that everything we return is on the right 40 | alignment to hold anything, including a double. */ 41 | #define ALIGNMENT sizeof(union alignment) 42 | 43 | /* We need to do this cast-to-char* trick on our pointers to adjust 44 | them; doing arithmetic on a void* is not standard. */ 45 | #define OUTPTR(ptr) (((char*)ptr)+ALIGNMENT) 46 | #define INPTR(ptr) (((char*)ptr)-ALIGNMENT) 47 | 48 | static size_t total_allocated = 0; 49 | static void *replacement_malloc(size_t sz) 50 | { 51 | void *chunk = malloc(sz + ALIGNMENT); 52 | if (!chunk) return chunk; 53 | total_allocated += sz; 54 | *(size_t*)chunk = sz; 55 | return OUTPTR(chunk); 56 | } 57 | static void *replacement_realloc(void *ptr, size_t sz) 58 | { 59 | size_t old_size = 0; 60 | if (ptr) { 61 | ptr = INPTR(ptr); 62 | old_size = *(size_t*)ptr; 63 | } 64 | ptr = realloc(ptr, sz + ALIGNMENT); 65 | if (!ptr) 66 | return NULL; 67 | *(size_t*)ptr = sz; 68 | total_allocated = total_allocated - old_size + sz; 69 | return OUTPTR(ptr); 70 | } 71 | static void replacement_free(void *ptr) 72 | { 73 | ptr = INPTR(ptr); 74 | total_allocated -= *(size_t*)ptr; 75 | free(ptr); 76 | } 77 | void start_counting_bytes(void) 78 | { 79 | event_set_mem_functions(replacement_malloc, 80 | replacement_realloc, 81 | replacement_free); 82 | } 83 | 84 | ``` 85 | 86 | 87 | ### 注意 88 | 89 | * 替换内存管理函数影响libevent 随后的所有分配、调整大小和释放内存操作。所以,必 90 | 须保证在调用任何其他libevent 函数之前进行替换。否则,libevent 可能用你的free 函 91 | 数释放用C 库的malloc 分配的内存。 92 | 93 | 94 | * 你的malloc 和realloc 函数返回的内存块应该具有和C 库返回的内存块一样的地址对 95 | 齐。 96 | 97 | 98 | * 你的realloc 函数应该正确处理realloc(NULL,sz)(也就是当作malloc(sz)处理) 99 | 100 | * 你的realloc 函数应该正确处理realloc(ptr,0)(也就是当作free(ptr)处理) 101 | 102 | * 你的free 函数不必处理free(NULL) 103 | 104 | * 你的malloc 函数不必处理malloc(0) 105 | 106 | * 如果在多个线程中使用libevent,替代的内存管理函数需要是线程安全的。 107 | 108 | * libevent 将使用这些函数分配返回给你的内存。所以,如果要释放由libevent 函数分配 109 | 和返回的内存,而你已经替换malloc 和realloc 函数,那么应该使用替代的free 函数。 110 | 111 | 112 | event_set_mem_functions 函数声明在中,在libevent 2.0.1-alpha 版本中 113 | 首次出现。 114 | 115 | 可以在禁止event_set_mem_functions 函数的配置下编译libevent 。这时候使用 116 | event_set_mem_functions 将不会编译或者链接。 117 | 118 | 在2.0.2-alpha 及以后版本中,可以通过检查是否定义了EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED 宏来确定event_set_mem_functions 函数是否存在。 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /43_shi_fang_event_base.md: -------------------------------------------------------------------------------- 1 | # 4.3 释放event_base 2 | 3 | 使用完 event_base 之后,使用 event_base_free()进行释放。 4 | 5 | ```cpp 6 | void event_base_free(struct event_base *base); 7 | ``` 8 | 9 | >注意:这个函数不会释放当前与 event_base 关联的任何事件,或者关闭他们的套接字 ,或 者释放任何指针。 10 | 11 | 12 | event_base_free()定义在中,首次由 libevent 1.2实现。 -------------------------------------------------------------------------------- /43_suo_he_xian_cheng_de_she_zhi.md: -------------------------------------------------------------------------------- 1 | # 9.4 锁和线程的设置 2 | 3 | 编写多线程程序的时候,在多个线程中同时访问同样的数据并不总是安全的。 4 | 5 | 6 | libevent 的结构体在多线程下通常有三种工作方式: 7 | 8 | * 某些结构体内在地是单线程的:同时在多个线程中使用它们总是不安全的。 9 | 10 | * 某些结构体具有可选的锁:可以告知 libevent 是否需要在多个线程中使用每个对象。 11 | 12 | * 某些结构体总是锁定的 :如果 libevent 在支持锁的配置下运行 ,在多个线程中使用它们 总是安全的。 13 | 14 | 15 | 为获取锁,在调用分配需要在多个线程间共享的结构体的 libevent 函数之前,必须告 知 libevent 使用哪个锁函数。 16 | 17 | 如果使用 pthreads 库,或者使用 Windows 本地线程代码,那么你是幸运的:已经有设 置 libevent 使用正确的 pthreads 或者 Windows 函数的预定义函数。 18 | 19 | 20 | ### 接口 21 | 22 | ```cpp 23 | #ifdef WIN32 24 | int evthread_use_windows_threads(void); 25 | #define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED 26 | #endif 27 | 28 | #ifdef _EVENT_HAVE_PTHREADS 29 | int evthread_use_pthreads(void); 30 | #define EVTHREAD_USE_PTHREADS_IMPLEMENTED 31 | 32 | #endif 33 | ``` 34 | 35 | 36 | 这些函数在成功时都返回 0,失败时返回 -1。 37 | 38 | 39 | 如果使用不同的线程库,则需要一些额外的工作,必须使用你的线程库来定义函数去实现: 40 | 41 | 42 | * 锁 43 | * 锁定 44 | * 解锁 45 | * 分配锁 46 | * 析构锁 47 | * 条件变量 48 | * 创建条件变量 49 | * 析构条件变量 50 | * 等待条件变量 51 | * 触发/广播某条件变量 52 | * 线程 53 | * 线程ID检测 54 | 55 | 56 | 使用 evthread_set_lock_callbacks 和 evthread_set_id_callback 接口告知 libevent 这些函数。 57 | 58 | 59 | ### 接口 60 | 61 | 62 | ```cpp 63 | #define EVTHREAD_WRITE 0x04 64 | #define EVTHREAD_READ 0x08 65 | #define EVTHREAD_TRY 0x10 66 | 67 | #define EVTHREAD_LOCKTYPE_RECURSIVE 1 68 | #define EVTHREAD_LOCKTYPE_READWRITE 2 69 | 70 | #define EVTHREAD_LOCK_API_VERSION 1 71 | 72 | struct evthread_lock_callbacks { 73 | int lock_api_version; 74 | unsigned supported_locktypes; 75 | void *(*alloc)(unsigned locktype); 76 | void (*free)(void *lock, unsigned locktype); 77 | int (*lock)(unsigned mode, void *lock); 78 | int (*unlock)(unsigned mode, void *lock); 79 | }; 80 | 81 | int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *); 82 | 83 | void evthread_set_id_callback(unsigned long (*id_fn)(void)); 84 | 85 | struct evthread_condition_callbacks { 86 | int condition_api_version; 87 | void *(*alloc_condition)(unsigned condtype); 88 | void (*free_condition)(void *cond); 89 | int (*signal_condition)(void *cond, int broadcast); 90 | int (*wait_condition)(void *cond, void *lock, 91 | const struct timeval *timeout); 92 | }; 93 | 94 | int evthread_set_condition_callbacks( 95 | const struct evthread_condition_callbacks *); 96 | ``` 97 | 98 | 99 | evthread_lock_callbacks 结构体描述的锁回调函数及其能力。对于上述版本, 100 | 101 | lock_api_version 字段必须设置为 EVTHREAD_LOCK_API_VERSION 。必须设置 supported_locktypes 字段为 EVTHREAD_LOCKTYPE_* 常量的组合以描述支持的锁类型 (在 2.0.4-alpha 版本中), 102 | 103 | EVTHREAD_LOCK_RECURSIVE 是必须的, 104 | 105 | EVTHREAD_LOCK_READWRITE 则没有使用)。 106 | 107 | alloc 函数必须返回指定类型的新锁 ; 108 | 109 | free 函数必须释放指定类型锁持有的所有资源 ; 110 | 111 | lock 函数必须试图以指定模式请求锁定 ,如果成 功则返回0,失败则返回非零; 112 | 113 | unlock 函数必须试图解锁,成功则返回 0,否则返回非零。 114 | 115 | 116 | 117 | ### 可识别的锁类型有: 118 | * 0:通常的,不必递归的锁。 119 | * EVTHREAD_LOCKTYPE_RECURSIVE :不会阻塞已经持有它的线程的锁 。一旦持有它的线程进行原来锁定次数的解锁,其他线程立刻就可以请求它了。 120 | 121 | 122 | * EVTHREAD_LOCKTYPE_READWRITE :可以让多个线程同时因为读而持有它 ,但是 任何时刻只有一个线程因为写而持有它。写操作排斥所有读操作。 123 | 124 | ### 可识别的锁模式有: 125 | 126 | * EVTHREAD_READ :仅用于读写锁:为读操作请求或者释放锁 127 | * EVTHREAD_WRITE :仅用于读写锁:为写操作请求或者释放锁 128 | * EVTHREAD_TRY :仅用于锁定:仅在可以立刻锁定的时候才请求锁定 129 | 130 | id_fn 参数必须是一个函数,它返回一个无符号长整数,标识调用此函数的线程。对于相同 线程,这个函数应该总是返回同样的值 ;而对于同时调用该函数的不同线程 ,必须返回不同 的值。 131 | 132 | 133 | 134 | vthread_condition_callbacks 结构体描述了与条件变量相关的回调函数。对于上述版本 , condition_api_version 字段必须设置为 EVTHREAD_CONDITION_API_VERSION 。 alloc_condition 函数必须返回到新条件变量的指针 。它接受0作为其参数。free_condition 函 数必须释放条件变量持有的存储器和资源。 wait_condition 函数要求三个参数:一个 由 alloc_condition 分配的条件变量 ,一个由你提供的 evthread_lock_callbacks.alloc 函数分配 的锁,以及一个可选的超时值 。调用本函数时 ,必须已经持有参数指定的锁 ;本函数应该释 放指定的锁,等待条件变量成为授信状态,或者直到指定的超时时间已经流逝(可选 )。 wait_condition 应该在错误时返回-1,条件变量授信时返回0,超时时返回1。返回之前,函 数应该确定其再次持有锁。最后, signal_condition 函数应该唤醒等待该条件变量的某个线 程(broadcast 参数为 false 时),或者唤醒等待条件变量的所有线程(broadcast 参数为 true 时)。只有在持有与条件变量相关的锁的时候,才能够进行这些操作。 135 | 136 | 137 | >关于条件变量的更多信息,请查看 pthreads 的 pthread_cond_*函数文档,或者 Windows 的 CONDITION_VARIABLE(Windows Vista 新引入的)函数文档。 138 | 139 | 140 | ### 实例: 141 | 142 | ```cpp 143 | 关于使用这些函数的示例, 144 | 145 | 请查看 Libevent 源代码发布版本中的 146 | evthread_pthread.c 和 evthread_win32.c 文件。 147 | ``` 148 | 149 | 150 | 这些函数在 中声明,其中大多数在 2.0.4-alpha 版本中首次出现。 2.0.1-alpha 到2.0.3-alpha 使用较老版本的锁函数 。event_use_pthreads 函数要求程序链接 到 event_pthreads 库。 151 | 152 | 153 | 条件变量函数是2.0.7-rc 版本新引入的,用于解决某些棘手的死锁问题。 154 | 155 | 可以创建禁止锁支持的libevent。这时候已创建的使用上述线程相关函数的程序将不能运行。 156 | 157 | 158 | 159 | ## 调试做的使用 160 | 161 | 为帮助调试锁的使用,libevent 有一个可选的“锁调试”特征。这个特征包装了锁调用,以便捕获典型的锁错误,包括: 162 | 163 | * 解锁并没有持有的锁 164 | * 重新锁定一个非递归锁 165 | 166 | 167 | 如果发生这些错误中的某一个, libevent 将给出断言失败并且退出。 168 | 169 | ```cpp 170 | void event_enable_debug_mode(void); 171 | ``` 172 | 173 | 必须在创建或者使用任何锁之前调用这个函数。为安全起见,请在设置完线程函数后立即调用这个函数。 174 | -------------------------------------------------------------------------------- /44_eventbase_you_xian_ji.md: -------------------------------------------------------------------------------- 1 | # 4.4 event_base优先级 2 | 3 | libevent支持为事件设置多个优先级。然而, event_base默认只支持单个优先级。可以调用 event_base_priority_init()设置 event_base 的优先级数目。 4 | 5 | ```cpp 6 | int event_base_priority_init(struct event_base *base, int n_priorities); 7 | ``` 8 | 9 | 成功时这个函数返回 0,失败时返回 -1。base 是要修改的 event_base,n_priorities 是要支 持的优先级数目,这个数目至少是 1 。每个新的事件可用的优先级将从 0 (最高) 到 n_priorities-1(最低)。 10 | 11 | 12 | 常量 EVENT_MAX_PRIORITIES 表示 n_priorities 的上限。调用这个函数时为 n_priorities 给出更大的值是错误的。 13 | 14 | >必须在任何事件激活之前调用这个函数,最好在创建 event_base 后立刻调用。 -------------------------------------------------------------------------------- /45_diao_shi_shi_jian_de_shi_yong.md: -------------------------------------------------------------------------------- 1 | # 9.5 调试事件的使用 2 | 3 | libevent 可以检测使用事件时的一些常见错误并且进行报告。这些错误包括: 4 | 5 | * 将未初始化的 event 结构体当作已经初始化的 6 | 7 | * 试图重新初始化未决的 event 结构体 8 | 9 | 10 | 11 | 跟踪哪些事件已经初始化需要使用额外的内存和处理器时间 ,所以只应该在真正调试程序的 时候才启用调试模式。 12 | 13 | ```cpp 14 | void event_enable_debug_mode(void); 15 | ``` 16 | 17 | 必须在创建任何 event_base 之前调用这个函数。 18 | 19 | 如果在调试模式下使用大量由 event_assign(而不是 event_new)创建的事件,程序可能 会耗尽内存,这是因为没有方式可以告知 libevent 由 event_assign 创建的事件不会再被使 用了(可以调用 event_free 告知由 event_new 创建的事件已经无效了 )。如果想在调试时 避免耗尽内存,可以显式告知 libevent 这些事件不再被当作已分配的了: 20 | 21 | ```cpp 22 | void event_debug_unassign(struct event *ev); 23 | ``` 24 | 25 | 没有启用调试的时候调用 event_debug_unassign 没有效果。 26 | 27 | 28 | ### 实例 29 | 30 | ```cpp 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | void cb(evutil_socket_t fd, short what, void *ptr) 37 | { 38 | /* We pass 'NULL' as the callback pointer for the heap allocated 39 | * event, and we pass the event itself as the callback pointer 40 | * for the stack-allocated event. */ 41 | struct event *ev = ptr; 42 | 43 | if (ev) 44 | event_debug_unassign(ev); 45 | } 46 | 47 | /* Here's a simple mainloop that waits until fd1 and fd2 are both 48 | * ready to read. */ 49 | void mainloop(evutil_socket_t fd1, evutil_socket_t fd2, int debug_mode) 50 | { 51 | struct event_base *base; 52 | struct event event_on_stack, *event_on_heap; 53 | 54 | if (debug_mode) 55 | event_enable_debug_mode(); 56 | 57 | base = event_base_new(); 58 | 59 | event_on_heap = event_new(base, fd1, EV_READ, cb, NULL); 60 | event_assign(&event_on_stack, base, fd2, EV_READ, cb, &event_on_stack); 61 | 62 | event_add(event_on_heap, NULL); 63 | event_add(&event_on_stack, NULL); 64 | 65 | event_base_dispatch(base); 66 | 67 | event_free(event_on_heap); 68 | event_base_free(base); 69 | } 70 | 71 | ``` -------------------------------------------------------------------------------- /45_eventbase_he_fork.md: -------------------------------------------------------------------------------- 1 | # 4.5 event_base和fork 2 | 3 | 不是所有事件后端都在调用 fork()之后可以正确工作。所以,如果在使用 fork()或者其 他相关系统调用启动新进程之后,希望在新进程中继续使用 event_base,就需要进行重新初始化。 4 | 5 | 6 | 7 | ```cpp 8 | int event_reinit(struct event_base *base); 9 | ``` 10 | 成功时这个函数返回 0,失败时返回 -1。 11 | 12 | 13 | ###实例 14 | ```cpp 15 | struct event_base *base = event_base_new(); 16 | 17 | /* ... add some events to the event_base ... */ 18 | 19 | if (fork()) { 20 | /* In parent */ 21 | continue_running_parent(base); /*...*/ 22 | } else { 23 | /* In child */ 24 | event_reinit(base); 25 | continue_running_child(base); /*...*/ 26 | } 27 | ``` 28 | 29 | event_reinit()定义在中,在 libevent 1.4.3-alpha 版中首次可用。 -------------------------------------------------------------------------------- /5-libevent编程api.md: -------------------------------------------------------------------------------- 1 | #4 event_base 2 | 3 | >本章主要来源《libevent参考手册(中文版)》。 4 | 5 | 6 | 7 | **使用 libevent 函数之前需要分配一个或者多个 event_base 结构体。每个 event_base 结构 体持有一个事件集合,可以检测以确定哪个事件是激活的。** 8 | 9 | 10 | 如果设置 event_base 使用锁,则可以安全地在多个线程中访问它 。然而,其事件循环只能 运行在一个线程中。如果需要用多个线程检测 IO,则需要为每个线程使用一个 event_base。 11 | 12 | 每个 event_base 都有一种用于检测哪种事件已经就绪的 “方法”,或者说后端。可以识别的方法有: 13 | 14 | * **select ** 15 | * poll 16 | * **epoll** 17 | * kqueue 18 | * devpoll 19 | * evport 20 | * win32 21 | 22 | 23 | -------------------------------------------------------------------------------- /51_yun_xing_xun_huan.md: -------------------------------------------------------------------------------- 1 | # 5.1 运行循环 2 | 3 | 一旦有了一个已经注册了某些事件的 event_base(关于如何创建和注册事件请看下一节 ), 就需要让 libevent 等待事件并且通知事件的发生。 4 | 5 | ```cpp 6 | #define EVLOOP_ONCE 0x01 7 | #define EVLOOP_NONBLOCK 0x02 8 | #define EVLOOP_NO_EXIT_ON_EMPTY 0x04 9 | 10 | int event_base_loop(struct event_base *base, int flags); 11 | ``` 12 | 13 | 默认情况下,event_base_loop()函数运行 event_base 直到其中没有已经注册的事件为止。执行循环的时候 ,函数重复地检查是否有任何已经注册的事件被触发 (比如说,读事件 的文件描述符已经就绪,可以读取了;或者超时事件的超时时间即将到达 )。如果有事件被触发,函数标记被触发的事件为 “激活的”,并且执行这些事件。 14 | 15 | 16 | 在 flags 参数中设置一个或者多个标志就可以改变 event_base_loop()的行为。如果设置了 EVLOOP_ONCE ,循环将等待某些事件成为激活的 ,执行激活的事件直到没有更多的事件可以执行,然会返回。如果设置了 EVLOOP_NONBLOCK,循环不会等待事件被触发: 循环将仅仅检测是否有事件已经就绪,可以立即触发,如果有,则执行事件的回调。 17 | 18 | 完成工作后,如果正常退出, event_base_loop()返回0;如果因为后端中的某些未处理 错误而退出,则返回 -1。 19 | 20 | 21 | 为帮助理解,这里给出 event_base_loop()的算法概要: 22 | 23 | ```cpp 24 | while (any events are registered with the loop, 25 | or EVLOOP_NO_EXIT_ON_EMPTY was set) { 26 | 27 | if (EVLOOP_NONBLOCK was set, or any events are already active) 28 | If any registered events have triggered, mark them active. 29 | else 30 | Wait until at least one event has triggered, and mark it active. 31 | 32 | for (p = 0; p < n_priorities; ++p) { 33 | if (any event with priority of p is active) { 34 | Run all active events with priority of p. 35 | break; /* Do not run any events of a less important priority */ 36 | } 37 | } 38 | 39 | if (EVLOOP_ONCE was set or EVLOOP_NONBLOCK was set) 40 | break; 41 | } 42 | ``` 43 | 44 | 为方便起见,也可以调用 45 | 46 | ```cpp 47 | int event_base_dispatch(struct event_base *base); 48 | ``` 49 | 50 | event_base_dispatch ()等同于没有设置标志的 event_base_loop ( )。所以, event_base_dispatch ()将一直运行,直到没有已经注册的事件了,或者调用 了 event_base_loopbreak()或者 event_base_loopexit()为止。 51 | 52 | 53 | 这些函数定义在中,从 libevent 1.0版就存在了。 -------------------------------------------------------------------------------- /52_ting_zhi_xun_huan.md: -------------------------------------------------------------------------------- 1 | # 5.2 停止循环 2 | 3 | 如果想在移除所有已注册的事件之前停止活动的事件循环,可以调用两个稍有不同的函数 。 4 | 5 | 6 | ```cpp 7 | int event_base_loopexit(struct event_base *base, 8 | const struct timeval *tv); 9 | int event_base_loopbreak(struct event_base *base); 10 | ``` 11 | 12 | **event_base_loopexit()**让 event_base 在给定时间之后停止循环。如果 tv 参数为 NULL, event_base 会立即停止循环,没有延时。 13 | 14 | 如果 event_base 当前正在执行任何激活事件的回调,则回调会继续运行,直到运行完所有激活事件的回调之才退出。 15 | 16 | **event_base_loopbreak ()**让 event_base 立即退出循环。它与 event_base_loopexit (base,NULL)的不同在于,如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出。 17 | 18 | 19 | >注意 event_base_loopexit(base,NULL) 和 event_base_loopbreak(base) 在事件循环没有运行时的行为不同:前者安排下一次事件循环在下一轮回调完成后立即停止(就好像带 EVLOOP_ONCE 标志调用一样);后者却仅仅停止当前正在运行的循环,如果事件循环没 有运行,则没有任何效果。 20 | 21 | 这两个函数都在成功时返回 0,失败时返回 -1。 22 | 23 | ###实例: 24 | ```cpp 25 | 26 | 27 | #include 28 | 29 | /* Here's a callback function that calls loopbreak */ 30 | void cb(int sock, short what, void *arg) 31 | { 32 | struct event_base *base = arg; 33 | event_base_loopbreak(base); 34 | } 35 | 36 | void main_loop(struct event_base *base, evutil_socket_t watchdog_fd) 37 | { 38 | struct event *watchdog_event; 39 | 40 | /* Construct a new event to trigger whenever there are any bytes to 41 | read from a watchdog socket. When that happens, we'll call the 42 | cb function, which will make the loop exit immediately without 43 | running any other active events at all. 44 | */ 45 | watchdog_event = event_new(base, watchdog_fd, EV_READ, cb, base); 46 | 47 | event_add(watchdog_event, NULL); 48 | 49 | event_base_dispatch(base); 50 | } 51 | 52 | ``` 53 | 54 | ###实例2:执行事件循环10秒,然后退出 55 | 56 | ```cpp 57 | #include 58 | 59 | void run_base_with_ticks(struct event_base *base) 60 | { 61 | struct timeval ten_sec; 62 | 63 | ten_sec.tv_sec = 10; 64 | ten_sec.tv_usec = 0; 65 | 66 | /* Now we run the event_base for a series of 10-second intervals, printing 67 | "Tick" after each. For a much better way to implement a 10-second 68 | timer, see the section below about persistent timer events. */ 69 | while (1) { 70 | /* This schedules an exit ten seconds from now. */ 71 | event_base_loopexit(base, &ten_sec); 72 | 73 | event_base_dispatch(base); 74 | puts("Tick"); 75 | } 76 | } 77 | ``` 78 | 79 | 80 | 有时候需要知道对 event_base_dispatch()或者 event_base_loop()的调用是正常退出 的,还是因为调用 event_base_loopexit()或者 event_base_break()而退出的。可以调 用下述函数来确定是否调用了 loopexit 或者 break函数。 81 | 82 | ```cpp 83 | int event_base_got_exit(struct event_base *base); 84 | int event_base_got_break(struct event_base *base); 85 | ``` 86 | 87 | 这两个函数分别会在循环是因为调用 event_base_loopexit()或者 event_base_break()而退出的时候返回 true,否则返回 false。下次启动事件循环的时候,这些值会被重设。 88 | 89 | 这些函数声明在中。 90 | 91 | 92 | event_break_loopexit()函数首次在 libevent 1.0c 版本 中实现; 93 | event_break_loopbreak()首次在 libevent 1.4.3版本中实现。 94 | -------------------------------------------------------------------------------- /53_zhuan_chu_event_base_de_zhuang_tai.md: -------------------------------------------------------------------------------- 1 | # 5.3 转储event_base的状态 2 | 3 | 为帮助调试程序(或者调试 libevent),有时候可能需要加入到 event_base 的事件及其状态 的完整列表。调用 event_base_dump_events()可以将这个列表输出到指定的文件中。 4 | 5 | 6 | ```cpp 7 | void event_base_dump_events(struct event_base *base, FILE *f); 8 | ``` 9 | 10 | 这个列表是人可读的,未来版本的 libevent 将会改变其格式。 11 | 12 | -------------------------------------------------------------------------------- /5_eventloop_shi_jian_xun_huan.md: -------------------------------------------------------------------------------- 1 | # 5 事件循环event_loop 2 | 3 | 4 | -------------------------------------------------------------------------------- /61_chuang_jian_shi_jian.md: -------------------------------------------------------------------------------- 1 | # 6.1 创建事件 2 | 3 | ##6.1.1 生成新事件 4 | 5 | 使用 event_new()接口创建事件。 6 | 7 | ```cpp 8 | #define EV_TIMEOUT 0x01 9 | #define EV_READ 0x02 10 | #define EV_WRITE 0x04 11 | #define EV_SIGNAL 0x08 12 | #define EV_PERSIST 0x10 13 | #define EV_ET 0x20 14 | 15 | typedef void (*event_callback_fn)(evutil_socket_t, short, void *); 16 | 17 | struct event *event_new(struct event_base *base, evutil_socket_t fd, 18 | short what, event_callback_fn cb, 19 | void *arg); 20 | 21 | void event_free(struct event *event); 22 | ``` 23 | 24 | event_new()试图分配和构造一个用于 base 的新的事件。 25 | what 参数是上述标志的集合。 26 | 27 | 如果 fd 非负,则它是将被观察其读写事件的文件。 28 | 29 | 事件被激活时, libevent 将调用 cb 函数, 30 | 31 | 传递这些参数:文件描述符 fd,表示所有被触发事件的位字段 ,以及构造事件时的 arg 参数。 32 | 33 | 34 | 发生内部错误,或者传入无效参数时, event_new()将返回 NULL。 35 | 36 | 37 | >所有新创建的事件都处于已初始化和非未决状态 ,调用 event_add()可以使其成为未决的。 38 | 39 | 40 | 要释放事件,调用 event_free()。对未决或者激活状态的事件调用 event_free()是安全 的:在释放事件之前,函数将会使事件成为非激活和非未决的。 41 | 42 | ###实例: 43 | 44 | ```cpp 45 | #include 46 | 47 | void cb_func(evutil_socket_t fd, short what, void *arg) 48 | { 49 | const char *data = arg; 50 | printf("Got an event on socket %d:%s%s%s%s [%s]", 51 | (int) fd, 52 | (what&EV_TIMEOUT) ? " timeout" : "", 53 | (what&EV_READ) ? " read" : "", 54 | (what&EV_WRITE) ? " write" : "", 55 | (what&EV_SIGNAL) ? " signal" : "", 56 | data); 57 | } 58 | 59 | void main_loop(evutil_socket_t fd1, evutil_socket_t fd2) 60 | { 61 | struct event *ev1, *ev2; 62 | struct timeval five_seconds = {5,0}; 63 | struct event_base *base = event_base_new(); 64 | 65 | /* The caller has already set up fd1, fd2 somehow, and make them 66 | nonblocking. */ 67 | 68 | ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, cb_func, 69 | (char*)"Reading event"); 70 | ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, cb_func, 71 | (char*)"Writing event"); 72 | 73 | event_add(ev1, &five_seconds); 74 | event_add(ev2, NULL); 75 | event_base_dispatch(base); 76 | } 77 | ``` 78 | 79 | 上述函数定义在 中,首次出现在 libevent 2.0.1-alpha 版本中。 event_callback_fn 类型首次在2.0.4-alpha 版本中作为 typedef 出现。 80 | 81 | 82 | ## 6.1.2 事件标志 83 | 84 | * EV_TIMEOUT 85 | 86 | 这个标志表示某超时时间流逝后事件成为激活的。构造事件的时候,EV_TIMEOUT 标志是 被忽略的:可以在添加事件的时候设置超时 ,也可以不设置。超时发生时,回调函数的 what 参数将带有这个标志。 87 | 88 | * EV_READ 89 | 90 | 表示指定的文件描述符已经就绪,可以读取的时候,事件将成为激活的。 91 | 92 | * EV_WRITE 93 | 94 | 表示指定的文件描述符已经就绪,可以写入的时候,事件将成为激活的。 95 | 96 | * EV_SIGNAL 97 | 用于实现信号检测,请看下面的 “构造信号事件”节。 98 | 99 | * EV_PERSIST 100 | 表示事件是“持久的”,请看下面的“关于事件持久性”节。 101 | 102 | * EV_ET 103 | 104 | 表示如果底层的 event_base 后端支持边沿触发事件,则事件应该是边沿触发的。这个标志 影响 EV_READ 和 EV_WRITE 的语义。 105 | 106 | 107 | 从2.0.1-alpha 版本开始,可以有任意多个事件因为同样的条件而未决。比如说,可以有两 个事件因为某个给定的 fd 已经就绪,可以读取而成为激活的。这种情况下,多个事件回调 被执行的次序是不确定的。 108 | 109 | >这些标志定义在中。除了 EV_ET 在2.0.1-alpha 版本中引入外,所有标志 从1.0版本开始就存在了。 110 | 111 | 112 | ## 6.1.3 关于事件持久性 113 | 114 | 默认情况下,每当未决事件成为激活的(因为 fd 已经准备好读取或者写入,或者因为超时), 事件将在其回调被执行前成为非未决的。如果想让事件再次成为未决的 ,可以在回调函数中 再次对其调用 event_add()。 115 | 116 | 117 | 然而,如果设置了 EV_PERSIST 标志,事件就是持久的。这意味着即使其回调被激活 ,事件还是会保持为未决状态 。如果想在回调中让事件成为非未决的 ,可以对其调用 event_del ()。 118 | 119 | 每次执行事件回调的时候,持久事件的超时值会被复位。因此,如果具有 EV_READ|EV_PERSIST 标志,以及5秒的超时值,则事件将在以下情况下成为激活的: 120 | 121 | * 套接字已经准备好被读取的时候 122 | * 从最后一次成为激活的开始,已经逝去 5秒 123 | 124 | ## 6.1.4 信号事件 125 | libevent 也可以监测 POSIX 风格的信号。要构造信号处理器,使用: 126 | 127 | ```cpp 128 | #define evsignal_new(base, signum, cb, arg) \ 129 | event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg) 130 | ``` 131 | 132 | 除了提供一个信号编号代替文件描述符之外,各个参数与 event_new()相同。 133 | 134 | 135 | ###实例 136 | ```cpp 137 | struct event *hup_event; 138 | struct event_base *base = event_base_new(); 139 | 140 | /* call sighup_function on a HUP signal */ 141 | hup_event = evsignal_new(base, SIGHUP, sighup_function, NULL); 142 | ``` 143 | 144 | >注意 :信号回调是信号发生后在事件循环中被执行的,所以可以安全地调用通常不能 在 POSIX 风格信号处理器中使用的函数。 145 | 146 | 147 | **`警告`:不要在信号事件上设置超时,这可能是不被支持的。 [待修正:真是这样的吗?]** 148 | 149 | libevent 也提供了一组方便使用的宏用于处理信号事件: 150 | 151 | ```cpp 152 | #define evsignal_add(ev, tv) \ 153 | event_add((ev),(tv)) 154 | #define evsignal_del(ev) \ 155 | event_del(ev) 156 | #define evsignal_pending(ev, what, tv_out) \ 157 | event_pending((ev), (what), (tv_out)) 158 | ``` 159 | evsignal_*宏从2.0.1-alpha 版本开始存在。先前版本中这些宏叫做 signal_add()、signal_del ()等等。 160 | 161 | ### 关于信号的警告 162 | 163 | 在当前版本的 libevent 和大多数后端中,每个进程任何时刻只能有一个 event_base 可以监 听信号。如果同时向两个 event_base 添加信号事件,即使是不同的信号,也只有一 个 event_base 可以取得信号。 164 | kqueue 后端没有这个限制。 -------------------------------------------------------------------------------- /62_shi_jian_de_wei_jue_he_fei_wei_jue.md: -------------------------------------------------------------------------------- 1 | # 6.2 事件的未决和非未决 2 | 构造事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到 event_base。 3 | 4 | ## 6.2.1 设置未决事件 5 | 6 | 7 | ```cpp 8 | int event_add(struct event *ev, const struct timeval *tv); 9 | ``` 10 | 11 | 在非未决的事件上调用 event_add()将使其在配置的 event_base 中成为未决的。成功时 函数返回0,失败时返回-1。 12 | 13 | 如果 tv 为 NULL,添加的事件不会超时。否则, tv 以秒和微秒指定超时值。 14 | 15 | 如果对已经未决的事件调用 event_add(),事件将保持未决状态,并在指定的超时时间被重新调度。 16 | 17 | `注意 `:不要设置 tv 为希望超时事件执行的时间。如果在 2010 年 1 月 1 日设置 “tv->tv_sec=time(NULL)+10;”,超时事件将会等待40年,而不是10秒。 18 | 19 | ## 6.2.2 设置非未决事件 20 | 21 | ```cpp 22 | int event_del(struct event *ev); 23 | ``` 24 | 25 | 对已经初始化的事件调用 event_del()将使其成为非未决和非激活的。如果事件不是未决的或者激活的,调用将没有效果。成功时函数返回 0,失败时返回-1。 26 | 27 | `注意`:如果在事件激活后,其回调被执行前删除事件,回调将不会执行。 28 | 29 | 30 | 这些函数定义在中,从0.1版本就存在了。 31 | -------------------------------------------------------------------------------- /63_shi_jian_de_you_xian_ji.md: -------------------------------------------------------------------------------- 1 | # 6.3 事件的优先级 2 | 3 | 多个事件同时触发时,libevent 没有定义各个回调的执行次序。可以使用优先级来定义某些事件比其他事件更重要。 4 | 5 | 在前一章讨论过,每个 event_base 有与之相关的一个或者多个优先级。在初始化事件之后, 但是在添加到 event_base 之前,可以为其设置优先级。 6 | 7 | ```cpp 8 | int event_priority_set(struct event *event, int priority); 9 | ``` 10 | 11 | 事件的优先级是一个在 0和 event_base 的优先级减去1之间的数值。成功时函数返回 0,失 败时返回-1。 12 | 13 | 多个不同优先级的事件同时成为激活的时候 ,低优先级的事件不会运行 。libevent 会执行高优先级的事件,然后重新检查各个事件。只有在没有高优先级的事件是激活的时候 ,低优先级的事件才会运行。 14 | 15 | ###实例: 16 | ```cpp 17 | #include 18 | 19 | void read_cb(evutil_socket_t, short, void *); 20 | void write_cb(evutil_socket_t, short, void *); 21 | 22 | void main_loop(evutil_socket_t fd) 23 | { 24 | struct event *important, *unimportant; 25 | struct event_base *base; 26 | 27 | base = event_base_new(); 28 | event_base_priority_init(base, 2); 29 | /* Now base has priority 0, and priority 1 */ 30 | important = event_new(base, fd, EV_WRITE|EV_PERSIST, write_cb, NULL); 31 | unimportant = event_new(base, fd, EV_READ|EV_PERSIST, read_cb, NULL); 32 | event_priority_set(important, 0); 33 | event_priority_set(unimportant, 1); 34 | 35 | /* Now, whenever the fd is ready for writing, the write callback will 36 | happen before the read callback. The read callback won't happen at 37 | all until the write callback is no longer active. */ 38 | } 39 | ``` 40 | 41 | 如果不为事件设置优先级,则默认的优先级将会是 event_base 的优先级数目除以2。 -------------------------------------------------------------------------------- /64_jian_cha_shi_jian_zhuang_tai.md: -------------------------------------------------------------------------------- 1 | # 6.4 检查事件状态 2 | 3 | 有时候需要了解事件是否已经添加,检查事件代表什么。 4 | 5 | 6 | ```cpp 7 | int event_pending(const struct event *ev, short what, struct timeval *tv_out); 8 | 9 | #define event_get_signal(ev) /* ... */ 10 | evutil_socket_t event_get_fd(const struct event *ev); 11 | struct event_base *event_get_base(const struct event *ev); 12 | short event_get_events(const struct event *ev); 13 | event_callback_fn event_get_callback(const struct event *ev); 14 | void *event_get_callback_arg(const struct event *ev); 15 | int event_get_priority(const struct event *ev); 16 | 17 | void event_get_assignment(const struct event *event, 18 | struct event_base **base_out, 19 | evutil_socket_t *fd_out, 20 | short *events_out, 21 | event_callback_fn *callback_out, 22 | void **arg_out); 23 | ``` 24 | 25 | event_pending()函数确定给定的事件是否是未决的或者激活的。如果是,而且 what 参 数设置了 EV_READ、EV_WRITE、EV_SIGNAL 或者 EV_TIMEOUT 等标志,则函数会返回事件当前为之未决或者激活的所有标志 。如果提供了 tv_out 参数,并且 what 参数中设置了 EV_TIMEOUT 标志,而事件当前正因超时事件而未决或者激活,则 tv_out 会返回事件 的超时值。 26 | 27 | 28 | 29 | event_get_fd()和 event_get_signal()返回为事件配置的文件描述符或者信号值。 event_get_base()返回为事件配置的 event_base。event_get_events()返回事件的标志(EV_READ、EV_WRITE 等)。event_get_callback()和 event_get_callback_arg() 返回事件的回调函数及其参数指针。 30 | 31 | 32 | event_get_assignment()复制所有为事件分配的字段到提供的指针中。任何为 NULL 的参数会被忽略。 33 | 34 | ###实例 35 | 36 | ```cpp 37 | #include 38 | #include 39 | 40 | /* Change the callback and callback_arg of 'ev', which must not be 41 | * pending. */ 42 | int replace_callback(struct event *ev, event_callback_fn new_callback, 43 | void *new_callback_arg) 44 | { 45 | struct event_base *base; 46 | evutil_socket_t fd; 47 | short events; 48 | 49 | int pending; 50 | 51 | pending = event_pending(ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT, 52 | NULL); 53 | if (pending) { 54 | /* We want to catch this here so that we do not re-assign a 55 | * pending event. That would be very very bad. */ 56 | fprintf(stderr, 57 | "Error! replace_callback called on a pending event!\n"); 58 | return -1; 59 | } 60 | 61 | event_get_assignment(ev, &base, &fd, &events, 62 | NULL /* ignore old callback */ , 63 | NULL /* ignore old callback argument */); 64 | 65 | event_assign(ev, base, fd, events, new_callback, new_callback_arg); 66 | return 0; 67 | } 68 | ``` 69 | 70 | -------------------------------------------------------------------------------- /65_yi_ci_hong_fa_shi_jian.md: -------------------------------------------------------------------------------- 1 | # 6.5 一次触发事件 2 | 3 | 4 | 如果不需要多次添加一个事件,或者要在添加后立即删除事件,而事件又不需要是持久的 , 则可以使用 event_base_once()。 5 | 6 | ```cpp 7 | int event_base_once(struct event_base *, evutil_socket_t, short, 8 | void (*)(evutil_socket_t, short, void *), void *, const struct timeval *); 9 | ``` 10 | 11 | 除了不支持 EV_SIGNAL 或者 EV_PERSIST 之外,这个函数的接口与 event_new()相同。 安排的事件将以默认的优先级加入到 event_base并执行。回调被执行后,libevent内部将 会释放 event 结构。成功时函数返回0,失败时返回-1。 12 | 13 | 不能删除或者手动激活使用 event_base_once ()插入的事件:如果希望能够取消事件, 应该使用 event_new()或者 event_assign()。 14 | -------------------------------------------------------------------------------- /66_shou_dong_ji_huo_shi_jian.md: -------------------------------------------------------------------------------- 1 | # 6.6 手动激活事件 2 | 3 | 极少数情况下,需要在事件的条件没有触发的时候让事件成为激活的。 4 | 5 | ```cpp 6 | void event_active(struct event *ev, int what, short ncalls); 7 | ``` 8 | 9 | 这个函数让事件 ev 带有标志 what(EV_READ、EV_WRITE 和 EV_TIMEOUT 的组合)成 为激活的。事件不需要已经处于未决状态,激活事件也不会让它成为未决的。 -------------------------------------------------------------------------------- /67_shi_jian_zhuang_tai_zhi_jian_de_zhuan_huan.md: -------------------------------------------------------------------------------- 1 | # 6.7 事件状态之间的转换 2 | 3 | ![](libevent-6-事件状态图.png) -------------------------------------------------------------------------------- /6_shi_jian.md: -------------------------------------------------------------------------------- 1 | # 6 事件event 2 | 3 | libevent 的基本操作单元是事件。每个事件代表一组条件的集合,这些条件包括: 4 | 5 | * 文件描述符已经就绪,可以读取或者写入 6 | * 文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO) 7 | * 超时事件 8 | * 发生某信号 9 | * 用户触发事件 10 | 11 | 12 | 所有事件具有相似的生命周期。调用 libevent 函数设置事件并且关联到 event_base 之后, 事件进入“已初始化(initialized)”状态。此时可以将事件添加到 event_base 中,这使之进入“未决(pending)”状态。在未决状态下,如果触发事件的条件发生(比如说,文件描述 符的状态改变,或者超时时间到达 ),则事件进入“激活(active)”状态,(用户提供的)事件回调函数将被执行。如果配置为“持久的(persistent)”,事件将保持为未决状态。否则, 执行完回调后,事件不再是未决的。删除操作可以让未决事件成为非未决(已初始化)的 ; 添加操作可以让非未决事件再次成为未决的。 -------------------------------------------------------------------------------- /71_hui_diao_he_shui_wei.md: -------------------------------------------------------------------------------- 1 | # 7.1 回调和水位 2 | 3 | 每个 bufferevent 有两个数据相关的回调:一个读取回调和一个写入回调。 4 | 5 | 默认情况下,从底层传输端口读取了任意量的数据之后会调用读取回调 ; 6 | 7 | 输出缓冲区中足够量的数据被清空到底层传输端口后写入回调会被调用。 8 | 9 | 通过调整 bufferevent 的读取和写入 “水位 (watermarks )”可以覆盖这些函数的默认行为。 10 | 11 | 每个 bufferevent 有四个水位: 12 | 13 | * `读取低水位` :读取操作使得输入缓冲区的数据量在此级别或者更高时 ,读取回调将被调用。默认值为 0,所以每个读取操作都会导致读取回调被调用。 14 | 15 | 16 | * `读取高水位` :输入缓冲区中的数据量达到此级别后, bufferevent 将停止读取,直到输入缓冲区中足够量的数据被抽取 ,使得数据量低于此级别 。默认值是无限 ,所以永远不会因为输入缓冲区的大小而停止读取。 17 | 18 | 19 | * `写入低水位` :写入操作使得输出缓冲区的数据量达到或者低于此级别时 ,写入回调将被调用。默认值是 0,所以只有输出缓冲区空的时候才会调用写入回调。 20 | 21 | 22 | * `写入高水位` :bufferevent 没有直接使用这个水位。它在 bufferevent 用作另外一 个 bufferevent 的底层传输端口时有特殊意义。请看后面关于过滤型 bufferevent 的介绍。 23 | 24 | 25 | -------------------------------------------------------------------------------- /72_yan_chi_hui_diao.md: -------------------------------------------------------------------------------- 1 | # 7.2 延迟回调 2 | 3 | 默认情况下,bufferevent 的回调在相应的条件发生时立即被执行 。(evbuffer 的回调也是这样的,随后会介绍)在依赖关系复杂的情况下 ,这种立即调用会制造麻烦 。比如说,假如某个回调在 evbuffer A 空的时候向其中移入数据 ,而另一个回调在 evbuffer A 满的时候从中取出数据。这些调用都是在栈上发生的,在依赖关系足够复杂的时候,有栈溢出的风险。 4 | 5 | 6 | 要解决此问题,可以请求 bufferevent(或者 evbuffer)延迟其回调。条件满足时,延迟回调不会立即调用,而是在 event_loop()调用中被排队,然后在通常的事件回调之后执行 。 7 | 8 | (延迟回调由 libevent 2.0.1-alpha 版引入) -------------------------------------------------------------------------------- /73_bufferevent_xuan_xiang_biao_zhi.md: -------------------------------------------------------------------------------- 1 | # 7.3 bufferevent 选项标志 2 | 3 | 4 | 创建 bufferevent 时可以使用一个或者多个标志修改其行为。可识别的标志有: 5 | 6 | * BEV_OPT_CLOSE_ON_FREE :释放 bufferevent 时关闭底层传输端口。这将关闭底层套接字,释放底层 bufferevent 等。 7 | 8 | 9 | * BEV_OPT_THREADSAFE :自动为 bufferevent 分配锁,这样就可以安全地在多个线程中使用 bufferevent。 10 | 11 | 12 | * BEV_OPT_DEFER_CALLBACKS :设置这个标志时, bufferevent 延迟所有回调,如上所述。 13 | 14 | 15 | * BEV_OPT_UNLOCK_CALLBACKS :默认情况下,如果设置 bufferevent 为线程安全 的,则 bufferevent 会在调用用户提供的回调时进行锁定。设置这个选项会让 libevent 在执行回调的时候不进行锁定。 16 | 17 | 18 | (BEV_OPT_UNLOCK_CALLBACKS 由2.0.5-beta 版引入,其他选项由 2.0.1-alpha 版引 入) 19 | -------------------------------------------------------------------------------- /74_shi_yong_bufferevent.md: -------------------------------------------------------------------------------- 1 | # 7.4 使用bufferevent 2 | 3 | 基于套接字的 bufferevent 是最简单的,它使用 libevent 的底层事件机制来检测底层网络套 接字是否已经就绪,可以进行读写操作,并且使用底层网络调用(如 readv 、 writev 、 WSASend、WSARecv)来发送和接收数据。 4 | 5 | ## 7.4.1 创建基于套接字的bufferevent 6 | 7 | 可以使用 bufferevent_socket_new()创建基于套接字的 bufferevent。 8 | 9 | ```cpp 10 | struct bufferevent *bufferevent_socket_new( 11 | struct event_base *base, 12 | evutil_socket_t fd, 13 | enum bufferevent_options options); 14 | ``` 15 | 16 | base 是 event_base,options 是表示 bufferevent 选项(BEV_OPT_CLOSE_ON_FREE 等) 的位掩码, fd是一个可选的表示套接字的文件描述符。如果想以后设置文件描述符,可以设置fd为-1。 17 | 18 | 成功时函数返回一个 bufferevent,失败则返回 NULL。 19 | 20 | ## 7.4.2 在bufferevent上启动链接 21 | 22 | ```cpp 23 | int bufferevent_socket_connect(struct bufferevent *bev, 24 | struct sockaddr *address, int addrlen); 25 | ``` 26 | 27 | address 和 addrlen 参数跟标准调用 connect()的参数相同。如果还没有为 bufferevent 设置套接字,调用函数将为其分配一个新的流套接字,并且设置为非阻塞的。 28 | 29 | 30 | 如果已经为 bufferevent 设置套接字,调用bufferevent_socket_connect() 将告知 libevent 套接字还未连接,直到连接成功之前不应该对其进行读取或者写入操作。 31 | 32 | 连接完成之前可以向输出缓冲区添加数据。 33 | 34 | 如果连接成功启动,函数返回 0;如果发生错误则返回 -1。 35 | 36 | ```cpp 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | void eventcb(struct bufferevent *bev, short events, void *ptr) 43 | { 44 | if (events & BEV_EVENT_CONNECTED) { 45 | /* We're connected to 127.0.0.1:8080. Ordinarily we'd do 46 | something here, like start reading or writing. */ 47 | } else if (events & BEV_EVENT_ERROR) { 48 | /* An error occured while connecting. */ 49 | } 50 | } 51 | 52 | int main_loop(void) 53 | { 54 | struct event_base *base; 55 | struct bufferevent *bev; 56 | struct sockaddr_in sin; 57 | 58 | base = event_base_new(); 59 | 60 | memset(&sin, 0, sizeof(sin)); 61 | sin.sin_family = AF_INET; 62 | sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ 63 | sin.sin_port = htons(8080); /* Port 8080 */ 64 | 65 | bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 66 | 67 | bufferevent_setcb(bev, NULL, NULL, eventcb, NULL); 68 | 69 | if (bufferevent_socket_connect(bev, 70 | (struct sockaddr *)&sin, sizeof(sin)) < 0) { 71 | /* Error starting connection */ 72 | bufferevent_free(bev); 73 | return -1; 74 | } 75 | 76 | event_base_dispatch(base); 77 | return 0; 78 | } 79 | ``` 80 | 81 | 82 | **`注意`:如果使用 bufferevent_socket_connect() 发起连接,将只会收 到 BEV_EVENT_CONNECTED 事件。如果自己调用 connect(),则连接上将被报告为写入事 件。** 83 | 84 | 85 | -------------------------------------------------------------------------------- /751_shi_fang_bufferevent_cao_zuo.md: -------------------------------------------------------------------------------- 1 | # 7.5.1 释放bufferevent操作 2 | 3 | ```cpp 4 | void bufferevent_free(struct bufferevent *bev); 5 | ``` 6 | 7 | 这个函数释放 bufferevent。bufferevent 内部具有引用计数,所以,如果释放 时还有未决的延迟回调,则在回调完成之前 bufferevent 不会被删除。 8 | 9 | 如果设置了 BEV_OPT_CLOSE_ON_FREE 标志,并且 bufferevent 有一个套接字或者底层 bufferevent 作为其传输端口,则释放 bufferevent 将关闭这个传输端口。 -------------------------------------------------------------------------------- /752_cao_zuo_hui_diao_3001_shui_wei_he_qi_7528_jin_.md: -------------------------------------------------------------------------------- 1 | # 7.5.2 操作回调、水位和启用/禁用 2 | 3 | ```cpp 4 | typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx); 5 | typedef void (*bufferevent_event_cb)(struct bufferevent *bev, 6 | short events, void *ctx); 7 | 8 | void bufferevent_setcb(struct bufferevent *bufev, 9 | bufferevent_data_cb readcb, bufferevent_data_cb writecb, 10 | bufferevent_event_cb eventcb, void *cbarg); 11 | 12 | void bufferevent_getcb(struct bufferevent *bufev, 13 | bufferevent_data_cb *readcb_ptr, 14 | bufferevent_data_cb *writecb_ptr, 15 | bufferevent_event_cb *eventcb_ptr, 16 | void **cbarg_ptr); 17 | ``` 18 | 19 | bufferevent_setcb()函数修改 bufferevent 的一个或者多个回调 。readcb、writecb和eventcb函数将分别在已经读取足够的数据 、已经写入足够的数据 ,或者发生错误时被调用 。 20 | 21 | 每个回调函数的第一个参数都是发生了事件的bufferevent ,最后一个参数都是调用bufferevent_setcb()时用户提供的 cbarg 参数:可以通过它向回调传递数据。事件回调 的 events 参数是一个表示事件标志的位掩码:请看前面的 “回调和水位”节。 22 | 23 | 24 | 要禁用回调,传递 NULL 而不是回调函数 。注意:bufferevent 的所有回调函数共享单 个 cbarg, 所以修改它将影响所有回调函数。 25 | 26 | 27 | 28 | ###接口 29 | ```cpp 30 | void bufferevent_enable(struct bufferevent *bufev, short events); 31 | void bufferevent_disable(struct bufferevent *bufev, short events); 32 | 33 | short bufferevent_get_enabled(struct bufferevent *bufev); 34 | ``` 35 | 可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。没有启用读取或者写入事件时, bufferevent 将不会试图进行数据读取或者写入。 36 | 37 | 没有必要在输出缓冲区空时禁用写入事件: bufferevent 将自动停止写入,然后在有数据等 待写入时重新开始。 38 | 39 | 类似地,没有必要在输入缓冲区高于高水位时禁用读取事件 :bufferevent 将自动停止读取, 然后在有空间用于读取时重新开始读取。 40 | 41 | 默认情况下,新创建的 bufferevent 的写入是启用的,但是读取没有启用。 42 | 可以调用 bufferevent_get_enabled()确定 bufferevent 上当前启用的事件。 43 | 44 | ###接口 45 | ```cpp 46 | void bufferevent_setwatermark(struct bufferevent *bufev, short events, 47 | size_t lowmark, size_t highmark); 48 | ``` 49 | bufferevent_setwatermark()函数调整单个 bufferevent 的读取水位、写入水位,或者同时调 整二者。(如果 events 参数设置了 EV_READ,调整读取水位。如果 events 设置了 EV_WRITE 标志,调整写入水位) 50 | 51 | 对于高水位,0表示“无限”。 52 | 53 | ###示例 54 | 55 | ```cpp 56 | #include 57 | #include 58 | #include 59 | #include 60 | 61 | #include 62 | #include 63 | #include 64 | 65 | struct info { 66 | const char *name; 67 | size_t total_drained; 68 | }; 69 | 70 | void read_callback(struct bufferevent *bev, void *ctx) 71 | { 72 | struct info *inf = ctx; 73 | struct evbuffer *input = bufferevent_get_input(bev); 74 | size_t len = evbuffer_get_length(input); 75 | if (len) { 76 | inf->total_drained += len; 77 | evbuffer_drain(input, len); 78 | printf("Drained %lu bytes from %s\n", 79 | (unsigned long) len, inf->name); 80 | } 81 | } 82 | 83 | void event_callback(struct bufferevent *bev, short events, void *ctx) 84 | { 85 | struct info *inf = ctx; 86 | struct evbuffer *input = bufferevent_get_input(bev); 87 | int finished = 0; 88 | 89 | if (events & BEV_EVENT_EOF) { 90 | size_t len = evbuffer_get_length(input); 91 | printf("Got a close from %s. We drained %lu bytes from it, " 92 | "and have %lu left.\n", inf->name, 93 | (unsigned long)inf->total_drained, (unsigned long)len); 94 | finished = 1; 95 | } 96 | if (events & BEV_EVENT_ERROR) { 97 | printf("Got an error from %s: %s\n", 98 | inf->name, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); 99 | finished = 1; 100 | } 101 | if (finished) { 102 | free(ctx); 103 | bufferevent_free(bev); 104 | } 105 | } 106 | 107 | struct bufferevent *setup_bufferevent(void) 108 | { 109 | struct bufferevent *b1 = NULL; 110 | struct info *info1; 111 | 112 | info1 = malloc(sizeof(struct info)); 113 | info1->name = "buffer 1"; 114 | info1->total_drained = 0; 115 | 116 | /* ... Here we should set up the bufferevent and make sure it gets 117 | connected... */ 118 | 119 | /* Trigger the read callback only whenever there is at least 128 bytes 120 | of data in the buffer. */ 121 | bufferevent_setwatermark(b1, EV_READ, 128, 0); 122 | 123 | bufferevent_setcb(b1, read_callback, NULL, event_callback, info1); 124 | 125 | bufferevent_enable(b1, EV_READ); /* Start reading. */ 126 | return b1; 127 | } 128 | ``` -------------------------------------------------------------------------------- /753_cao_zuo_bufferevent_zhong_de_shu_ju.md: -------------------------------------------------------------------------------- 1 | # 7.5.3 操作bufferevent中的数据 2 | 3 | ##通过bufferevent得到evbuffer 4 | 如果只是通过网络读取或者写入数据 ,而不能观察操作过程,是没什么好处的。bufferevent 提供了下列函数用于观察要写入或者读取的数据。 5 | 6 | ```cpp 7 | struct evbuffer *bufferevent_get_input(struct bufferevent *bufev); 8 | struct evbuffer *bufferevent_get_output(struct bufferevent *bufev); 9 | ``` 10 | 11 | 这两个函数提供了非常强大的基础 :它们分别返回输入和输出缓冲区 。关于可以对 evbuffer 类型进行的所有操作的完整信息,请看下一章。 12 | 13 | 如果写入操作因为数据量太少而停止(或者读取操作因为太多数据而停止 ),则向输出缓冲 区添加数据(或者从输入缓冲区移除数据)将自动重启操作。 14 | 15 | ##向bufferevent的输出缓冲区添加数据 16 | 17 | ```cpp 18 | int bufferevent_write(struct bufferevent *bufev, 19 | const void *data, size_t size); 20 | int bufferevent_write_buffer(struct bufferevent *bufev, 21 | struct evbuffer *buf); 22 | ``` 23 | 24 | 这些函数向 bufferevent 的输出缓冲区添加数据。 bufferevent_write()将内存中从 data 处开 始的 size 字节数据添加到输出缓冲区的末尾 。bufferevent_write_buffer()移除 buf 的所有内 容,将其放置到输出缓冲区的末尾。成功时这些函数都返回 0,发生错误时则返回-1。 25 | 26 | 27 | ##从bufferevent的输入缓冲区移除数据 28 | ```cpp 29 | size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); 30 | int bufferevent_read_buffer(struct bufferevent *bufev, 31 | struct evbuffer *buf); 32 | ``` 33 | 34 | 这些函数从 bufferevent 的输入缓冲区移除数据。bufferevent_read()至多从输入缓冲区移除 size 字节的数据,将其存储到内存中 data 处。函数返回实际移除的字节数。 bufferevent_read_buffer()函数抽空输入缓冲区的所有内容,将其放置到 buf 中,成功时返 回0,失败时返回 -1。 35 | 36 | 注意,对于 bufferevent_read(),data 处的内存块必须有足够的空间容纳 size 字节数据。 37 | 38 | ###示例 39 | 40 | ```cpp 41 | #include 42 | #include 43 | 44 | #include 45 | 46 | void 47 | read_callback_uppercase(struct bufferevent *bev, void *ctx) 48 | { 49 | /* This callback removes the data from bev's input buffer 128 50 | bytes at a time, uppercases it, and starts sending it 51 | back. 52 | 53 | (Watch out! In practice, you shouldn't use toupper to implement 54 | a network protocol, unless you know for a fact that the current 55 | locale is the one you want to be using.) 56 | */ 57 | 58 | char tmp[128]; 59 | size_t n; 60 | int i; 61 | while (1) { 62 | n = bufferevent_read(bev, tmp, sizeof(tmp)); 63 | if (n <= 0) 64 | break; /* No more data. */ 65 | for (i=0; iother_bev)); 85 | } 86 | 87 | struct count { 88 | unsigned long last_fib[2]; 89 | }; 90 | 91 | void 92 | write_callback_fibonacci(struct bufferevent *bev, void *ctx) 93 | { 94 | /* Here's a callback that adds some Fibonacci numbers to the 95 | output buffer of bev. It stops once we have added 1k of 96 | data; once this data is drained, we'll add more. */ 97 | struct count *c = ctx; 98 | 99 | struct evbuffer *tmp = evbuffer_new(); 100 | while (evbuffer_get_length(tmp) < 1024) { 101 | unsigned long next = c->last_fib[0] + c->last_fib[1]; 102 | c->last_fib[0] = c->last_fib[1]; 103 | c->last_fib[1] = next; 104 | 105 | evbuffer_add_printf(tmp, "%lu", next); 106 | } 107 | 108 | /* Now we add the whole contents of tmp to bev. */ 109 | bufferevent_write_buffer(bev, tmp); 110 | 111 | /* We don't need tmp any longer. */ 112 | evbuffer_free(tmp); 113 | } 114 | ``` -------------------------------------------------------------------------------- /755_buffereventde_qing_kong_cao_zuo.md: -------------------------------------------------------------------------------- 1 | # 7.5.4 bufferevent的清空操作 2 | 3 | ```cpp 4 | int bufferevent_flush(struct bufferevent *bufev, 5 | short iotype, enum bufferevent_flush_mode state); 6 | ``` 7 | 8 | 清空 bufferevent 要求 bufferevent 强制从底层传输端口读取或者写入尽可能多的数据 ,而忽略其他可能保持数据不被写入的限制条件 。函数的细节功能依赖于 bufferevent 的具体类型。 9 | 10 | 11 | otype 参数应该是 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE,用于指示应该处 理读取、写入,还是二者都处理。 state 参数可以是 BEV_NORMAL、BEV_FLUSH 或者 BEV_FINISHED。BEV_FINISHED 指示应该告知另一端,没有更多数据需要发送了; 而 BEV_NORMAL 和 BEV_FLUSH 的区别依赖于具体的 bufferevent 类型。 12 | 13 | 14 | 失败时 bufferevent_flush()返回-1,如果没有数据被清空则返回 0,有数据被清空则返回 1。 -------------------------------------------------------------------------------- /75_tong_yong_bufferevent_cao_zuo.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /7_bufferevent.md: -------------------------------------------------------------------------------- 1 | # 7 数据缓冲Bufferevent 2 | 很多时候,除了响应事件之外,应用还希望做一定的数据缓冲。比如说,写入数据的时候 ,通常的运行模式是: 3 | 4 | * 决定要向连接写入一些数据,把数据放入到缓冲区中 5 | 6 | * 等待连接可以写入 7 | 8 | * 写入尽量多的数据 9 | 10 | * 记住写入了多少数据,如果还有更多数据要写入,等待连接再次可以写入 11 | 12 | 13 | 这种缓冲 IO 模式很通用,libevent 为此提供了一种通用机制,即bufferevent。 14 | 15 | bufferevent 由一个底层的传输端口(如套接字 ),一个读取缓冲区和一个写入缓冲区组成。与通常的事件在底层传输端口已经就绪,可以读取或者写入的时候执行回调不同的是,bufferevent 在读取或者写入了足够量的数据之后调用用户提供的回调。 16 | 17 | 有多种共享公用接口的 bufferevent 类型,编写本文时已存在以下类型: 18 | 19 | 20 | * `基于套接字的 bufferevent`:使用 event_*接口作为后端,通过底层流式套接字发送或者接收数据的 bufferevent 21 | 22 | 23 | * `异步 IO bufferevent`:使用 Windows IOCP 接口,通过底层流式套接字发送或者接收数据的 bufferevent(仅用于 Windows,试验中) 24 | 25 | 26 | * `过滤型 bufferevent`:将数据传输到底层 bufferevent 对象之前,处理输入或者输出数据的 bufferevent:比如说,为了压缩或者转换数据。 27 | 28 | 29 | * `成对的 bufferevent`:相互传输数据的两个 bufferevent。 30 | 31 | 32 | 33 | 34 | >`注意`:截止2.0.2-alpha 版,这里列出的 bufferevent 接口还没有完全正交于所有 的 bufferevent 类型。也就是说,下面将要介绍的接口不是都能用于所有bufferevent 类型。libevent 开发 者在未来版本中将修正这个问题。 35 | 36 | >`也请注意` :当前 bufferevent 只能用于像 TCP 这样的面向流的协议,将来才可能会支持 像 UDP 这样的面向数据报的协议。 37 | 38 | 39 | ## bufferevent和evbuffer 40 | 每个 bufferevent 都有一个输入缓冲区和一个输出缓冲区 ,它们的类型都是“struct evbuffer”。 有数据要写入到 bufferevent 时,添加数据到输出缓冲区 ;bufferevent 中有数据供读取的时候,从输入缓冲区抽取(drain)数据。 41 | evbuffer 接口支持很多种操作,后面的章节将讨论这些操作。 42 | -------------------------------------------------------------------------------- /81_chuang_jian_he_shi_fang_evbuffer.md: -------------------------------------------------------------------------------- 1 | # 8.1 创建和释放evbuffer 2 | 3 | ```cpp 4 | struct evbuffer *evbuffer_new(void); 5 | void evbuffer_free(struct evbuffer *buf); 6 | ``` 7 | 8 | 这两个函数的功能很简明: evbuffer_new() 分配和返回一个新的空 evbuffer ; 而 evbuffer_free()释放 evbuffer 和其内容。 9 | 10 | 11 | -------------------------------------------------------------------------------- /81_chuang_jianhe_shi_fang_evconnlistener.md: -------------------------------------------------------------------------------- 1 | # 8.1 创建和释放evconnlistener 2 | 3 | 4 | ```cpp 5 | struct evconnlistener * 6 | evconnlistener_new(struct event_base *base, 7 | evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, 8 | evutil_socket_t fd); 9 | 10 | struct evconnlistener * 11 | evconnlistener_new_bind(struct event_base *base, 12 | evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, 13 | const struct sockaddr *sa, int socklen); 14 | 15 | void evconnlistener_free(struct evconnlistener *lev); 16 | ``` 17 | 18 | 两个 evconnlistener_new*()函数都分配和返回一个新的连接监听器对象。连接监听器使 用 event_base 来得知什么时候在给定的监听套接字上有新的 TCP 连接。新连接到达时,监听 器调用你给出的回调函数。 19 | 20 | 两个函数中,base参数都是监听器用于监听连接的 event_base。cb是收到新连接时要调 用的回调函数; 21 | 22 | 如果 cb 为 NULL,则监听器是禁用的,直到设置了回调函数为止。 23 | 24 | ptr 指针将传递给回调函数。 25 | 26 | flags 参数控制回调函数的行为,下面会更详细论述。 27 | 28 | backlog 是任何 时刻网络栈允许处于还未接受状态的最大未决连接数。 29 | 30 | 更多细节请查看系统的 listen()函数文档。 31 | 32 | 如果 backlog 是负的,libevent 会试图挑选一个较好的值 ; 33 | 如果为0,libevent 认为已 经对提供的套接字调用了listen()。 34 | 35 | --- 36 | 37 | 两个函数的不同在于如何建立监听套接字。 38 | evconnlistener_new()函数假定已经将套接字绑定到要监听的端口,然后通过 fd 传入这个套接字。 39 | 40 | 如果要 libevent 分配和绑定套接字,可以调用 evconnlistener_new_bind() ,传输要绑定到的地址和地址长度。 41 | 42 | 43 | 要释放连接监听器,调用 evconnlistener_free()。 44 | 45 | ##可识别的标志 46 | 47 | 可以给 evconnlistener_new() 函数的 flags 参数传入一些标志。可以用或 (OR)运算任意连接 下述标志: 48 | 49 | * LEV_OPT_LEAVE_SOCKETS_BLOCKING 50 | 51 | 默认情况下,连接监听器接收新套接字后,会将其设置为非阻塞的,以便将其用于 libevent。如果不想要这种行为,可以设置这个标志。 52 | 53 | 54 | * LEV_OPT_CLOSE_ON_FREE 55 | 56 | 如果设置了这个选项,释放连接监听器会关闭底层套接字。 57 | 58 | * LEV_OPT_CLOSE_ON_EXEC 59 | 60 | 如果设置了这个选项,连接监听器会为底层套接字设置 close-on-exec 标志。更多信息请查 看 fcntl 和 FD_CLOEXEC 的平台文档。 61 | 62 | * LEV_OPT_REUSEABLE 63 | 64 | 某些平台在默认情况下 ,关闭某监听套接字后 ,要过一会儿其他套接字才可以绑定到同一个 端口。设置这个标志会让 libevent 标记套接字是可重用的,这样一旦关闭,可以立即打开其 他套接字,在相同端口进行监听。 65 | 66 | * LEV_OPT_THREADSAFE 67 | 68 | 为监听器分配锁,这样就可以在多个线程中安全地使用了。这是 2.0.8-rc 的新功能。 69 | 70 | 71 | ##链接监听器回调 72 | 73 | ```cpp 74 | typedef void (*evconnlistener_cb)(struct evconnlistener *listener, 75 | evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr); 76 | ``` 77 | 接收到新连接会调用提供的回调函数 。 78 | 79 | `listener` 参数是接收连接的连接监听器 。 80 | 81 | 82 | `sock` 参数是 新接收的套接字。 83 | 84 | `addr` 和 `len` 参数是接收连接的地址和地址长度。 85 | 86 | `ptr` 是调 用 evconnlistener_new() 时用户提供的指针。 87 | 88 | 89 | -------------------------------------------------------------------------------- /82_evbufferyu_xian_cheng_an_quan.md: -------------------------------------------------------------------------------- 1 | # 8.2 evbuffer与线程安全 2 | 3 | ```cpp 4 | int evbuffer_enable_locking(struct evbuffer *buf, void *lock); 5 | void evbuffer_lock(struct evbuffer *buf); 6 | void evbuffer_unlock(struct evbuffer *buf); 7 | ``` 8 | 9 | 默认情况下,在多个线程中同时访问 evbuffer 是不安全的。如果需要这样的访问,可以调 用 evbuffer_enable_locking() 。如果 lock 参数为 NULL , libevent 会使用evthread_set_lock_creation_callback 提供的锁创建函数创建一个锁 。否则,libevent 将 lock 参数用作锁。 10 | 11 | evbuffer_lock()和 evbuffer_unlock()函数分别请求和释放 evbuffer 上的锁。可以使用这两个 函数让一系列操作是原子的。如果 evbuffer 没有启用锁,这两个函数不做任何操作。 12 | 13 | 14 | (注意:对于单个操作,不需要调用 evbuffer_lock()和evbuffer_unlock(): 15 | 如果 evbuffer启用了锁,单个操作就已经是原子的 。只有在需要多个操作连续执行 ,不让其他线程介入的 时候,才需要手动锁定 evbuffer) 16 | 17 | -------------------------------------------------------------------------------- /82_qi_yong_he_jin_yong_evconnlistener.md: -------------------------------------------------------------------------------- 1 | # 8.2 启用和禁用 evconnlistener 2 | 3 | ```cpp 4 | int evconnlistener_disable(struct evconnlistener *lev); 5 | int evconnlistener_enable(struct evconnlistener *lev); 6 | ``` 7 | 8 | 这两个函数暂时禁止或者重新允许监听新连接。 -------------------------------------------------------------------------------- /83_diao_zheng_evconnlistener_de_hui_diao_han_shu.md: -------------------------------------------------------------------------------- 1 | # 8.3 调整 evconnlistener 的回调函数 2 | 3 | ```cpp 4 | void evconnlistener_set_cb(struct evconnlistener *lev, 5 | evconnlistener_cb cb, void *arg); 6 | ``` 7 | 8 | 函数调整 evconnlistener 的回调函数和其参数。它是 2.0.9-rc 版本引入的。 -------------------------------------------------------------------------------- /83_jian_cha_evbuffer.md: -------------------------------------------------------------------------------- 1 | # 8.3 检查evbuffer 2 | 3 | ```cpp 4 | size_t evbuffer_get_length(const struct evbuffer *buf); 5 | //这个函数返回 evbuffer 存储的字节数,它在2.0.1-alpha 版本中引入。 6 | ``` 7 | 8 | ```cpp 9 | size_t evbuffer_get_contiguous_space(const struct evbuffer *buf); 10 | //这个函数返回连续地存储在 evbuffer前面的字节数。 11 | //evbuffer中的数据可能存储在多个分隔开的内存块中, 12 | //这个函数返回当前第一个块中的字节数。 13 | ``` -------------------------------------------------------------------------------- /84_jian_ce_evconnlistener.md: -------------------------------------------------------------------------------- 1 | # 8.4 检测 evconnlistener 2 | 3 | ```cpp 4 | evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev); 5 | struct event_base *evconnlistener_get_base(struct evconnlistener *lev); 6 | ``` 7 | 8 | 这些函数分别返回监听器关联的套接字和 event_base。 -------------------------------------------------------------------------------- /84_xiang_evbuffer_tian_jia_shu_ju.md: -------------------------------------------------------------------------------- 1 | # 8.4 向evbuffer添加数据 2 | 3 | ```cpp 4 | int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); 5 | //这个函数添加 data 处的 datalen 字节到 buf 的末尾, 6 | //成功时返回0,失败时返回-1。 7 | ``` 8 | 9 | 10 | ```cpp 11 | int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) 12 | int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap); 13 | //这些函数添加格式化的数据到 buf 末尾。 14 | //格式参数和其他参数的处理分别与 C 库函数 printf 和 vprintf 相同。函数返回添加的字节数。 15 | ``` 16 | 17 | ```cpp 18 | int evbuffer_expand(struct evbuffer *buf, size_t datlen); 19 | //这个函数修改缓冲区的最后一块,或者添加一个新的块, 20 | //使得缓冲区足以容纳 datlen 字节, 而不需要更多的内存分配。 21 | ``` 22 | 23 | ###示例 24 | ```cpp 25 | /* Here are two ways to add "Hello world 2.0.1" to a buffer. */ 26 | /* Directly: */ 27 | evbuffer_add(buf, "Hello world 2.0.1", 17); 28 | 29 | /* Via printf: */ 30 | evbuffer_add_printf(buf, "Hello %s %d.%d.%d", "world", 2, 0, 1); 31 | ``` -------------------------------------------------------------------------------- /85_evbuffershu_ju_yi_dong.md: -------------------------------------------------------------------------------- 1 | # 8.5 evbuffer数据移动 2 | 3 | 为提高效率,libevent 具有将数据从一个 evbuffer 移动到另一个的优化函数。 4 | 5 | ```cpp 6 | int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src); 7 | 8 | int evbuffer_remove_buffer(struct evbuffer *src, 9 | struct evbuffer *dst, 10 | size_t datlen); 11 | ``` 12 | 13 | evbuffer_add_buffer()将 src 中的所有数据移动到 dst 末尾,成功时返回0,失败时返回-1。 14 | 15 | 16 | evbuffer_remove_buffer()函数从 src 中移动 datlen 字节到 dst 末尾,尽量少进行复制。如果字节数小于 datlen,所有字节被移动。函数返回移动的字节数。 17 | 18 | evbuffer_add_buffer()在0.8版本引入; evbuffer_remove_buffer()是2.0.1-alpha 版本新增加的。 -------------------------------------------------------------------------------- /85_zhen_ce_cuo_wu.md: -------------------------------------------------------------------------------- 1 | # 8.5 侦测错误 2 | 3 | 可以设置一个一旦监听器上的 accept()调用失败就被调用的错误回调函数 。对于一个不解决就会锁定进程的错误条件,这很重要。 4 | 5 | ```cpp 6 | typedef void (*evconnlistener_errorcb)(struct evconnlistener *lis, void *ptr); 7 | void evconnlistener_set_error_cb(struct evconnlistener *lev, 8 | evconnlistener_errorcb errorcb); 9 | ``` 10 | 11 | 如果使用 evconnlistener_set_error_cb() 为监听器设置了错误回调函数,则监听器发生错误 时回调函数就会被调用。 12 | 13 | 第一个参数是监听器, 14 | 15 | 第二个参数是调用 evconnlistener_new() 时传入的 ptr。 16 | -------------------------------------------------------------------------------- /86_tian_jia_shu_ju_dao_evbuffer_qian.md: -------------------------------------------------------------------------------- 1 | # 8.6 添加数据到evbuffer前 2 | 3 | -------------------------------------------------------------------------------- /8_evbuffer.md: -------------------------------------------------------------------------------- 1 | # 8 数据封装evBuffer 2 | 3 | libevent 的 evbuffer 实现了为向后面添加数据和从前面移除数据而优化的字节队列。 4 | 5 | evbuffer 用于处理缓冲网络 IO 的“缓冲”部分。它不提供调度 IO 或者当 IO 就绪时触发 IO 的 功能:这是 bufferevent 的工作。 6 | 7 | 除非特别说明,本章描述的函数都在 event2/buffer.h中声明。 -------------------------------------------------------------------------------- /8_lian_jie_jian_ting_qi_evconnlistener.md: -------------------------------------------------------------------------------- 1 | # 8 链接监听器evconnlistener 2 | 3 | evconnlistener 机制提供了监听和接受 TCP 连接的方法。 4 | 5 | 6 | 本章的所有函数和类型都在 event2/listener.h 中声明,除非特别说明。 7 | 8 | 9 | 它们都在2.0.2-alpha 版本中首次出现。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 《Libevent深入浅出》 2 | 3 | 本教程要求有一定的服务并发编程基础,了解select和epoll等多路I/O复用机制。 4 | 5 | 教程目的主要是快速建立libevent的认知,了解libevent的常用数据结构和编程方法。 6 | 7 | 达到可以使用libevent写出自己的高并发服务器处理模型。 8 | 9 | > ## 作者:刘丹冰 10 | > 11 | > 邮箱:danbing\_at@163.com 12 | 13 | ![](/assets/libevent深入浅出封面.jpg) 14 | 15 | --- 16 | # 目录 17 | 18 | * [Libevent深入浅出](README.md) 19 | * [1 Libevent官方](chapter1.md) 20 | * [2 epoll](2-epoll.md) 21 | * [2.1 流-IO操作-阻塞](21-流-io.md) 22 | * [2.2 解决阻塞死等待的办法](21-解决阻塞死等待的办法.md) 23 | * [2.3 什么是epoll](23-什么是epoll.md) 24 | * [2.4 epollAPI](24-epollapi.md) 25 | * [2.5 触发模式](25hong_fa_mo_5f0f_md.md) 26 | * [2.6 简单的epoll服务器](26-简单的epoll服务器.md) 27 | * [3 epoll和reactor](3-epoll和reactor.md) 28 | * [3.1 reactor反应堆模式](31_reactorfan_ying_dui_mo_shi.md) 29 | * [3.2 epoll的反应堆模式实现](32_epollde_fan_ying_dui_mo_shi_shi_xian.md) 30 | * [4 event_base](5-libevent编程api.md) 31 | * [4.1 创建event_base](41_jian_li_mo_ren_de_event_base.md) 32 | * [4.2 检查event_base后端](42_jian_cha_event_base_hou_duan.md) 33 | * [4.3 释放event_base](43_shi_fang_event_base.md) 34 | * [4.4 event_base优先级](44_eventbase_you_xian_ji.md) 35 | * [4.5 event_base和fork](45_eventbase_he_fork.md) 36 | * [5 事件循环event_loop](5_eventloop_shi_jian_xun_huan.md) 37 | * [5.1 运行循环](51_yun_xing_xun_huan.md) 38 | * [5.2 停止循环](52_ting_zhi_xun_huan.md) 39 | * [5.3 转储event_base的状态](53_zhuan_chu_event_base_de_zhuang_tai.md) 40 | * [6 事件event](6_shi_jian.md) 41 | * [6.1 创建事件](61_chuang_jian_shi_jian.md) 42 | * [6.2 事件的未决和非未决](62_shi_jian_de_wei_jue_he_fei_wei_jue.md) 43 | * [6.3 事件的优先级](63_shi_jian_de_you_xian_ji.md) 44 | * [6.4 检查事件状态](64_jian_cha_shi_jian_zhuang_tai.md) 45 | * [6.5 一次触发事件](65_yi_ci_hong_fa_shi_jian.md) 46 | * [6.6 手动激活事件](66_shou_dong_ji_huo_shi_jian.md) 47 | * [6.7 事件状态之间的转换](67_shi_jian_zhuang_tai_zhi_jian_de_zhuan_huan.md) 48 | * [7 数据缓冲Bufferevent](7_bufferevent.md) 49 | * [7.1 回调和水位](71_hui_diao_he_shui_wei.md) 50 | * [7.2 延迟回调](72_yan_chi_hui_diao.md) 51 | * [7.3 bufferevent 选项标志](73_bufferevent_xuan_xiang_biao_zhi.md) 52 | * [7.4 使用bufferevent](74_shi_yong_bufferevent.md) 53 | * [7.5 通用bufferevent操作](75_tong_yong_bufferevent_cao_zuo.md) 54 | * [7.5.1 释放bufferevent操作](751_shi_fang_bufferevent_cao_zuo.md) 55 | * [7.5.2 操作回调、水位和启用/禁用](752_cao_zuo_hui_diao_3001_shui_wei_he_qi_7528_jin_.md) 56 | * [7.5.3 操作bufferevent中的数据](753_cao_zuo_bufferevent_zhong_de_shu_ju.md) 57 | * [7.5.4 bufferevent的清空操作](755_buffereventde_qing_kong_cao_zuo.md) 58 | * [8 数据封装evBuffer](8_evbuffer.md) 59 | * [8.1 创建和释放evbuffer](81_chuang_jian_he_shi_fang_evbuffer.md) 60 | * [8.2 evbuffer与线程安全](82_evbufferyu_xian_cheng_an_quan.md) 61 | * [8.3 检查evbuffer](83_jian_cha_evbuffer.md) 62 | * [8.4 向evbuffer添加数据](84_xiang_evbuffer_tian_jia_shu_ju.md) 63 | * [8.5 evbuffer数据移动](85_evbuffershu_ju_yi_dong.md) 64 | * [8.6 添加数据到evbuffer前](86_tian_jia_shu_ju_dao_evbuffer_qian.md) 65 | * [8 链接监听器evconnlistener](8_lian_jie_jian_ting_qi_evconnlistener.md) 66 | * [8.1 创建和释放 evconnlistener](81_chuang_jianhe_shi_fang_evconnlistener.md) 67 | * [8.2 启用和禁用 evconnlistener](82_qi_yong_he_jin_yong_evconnlistener.md) 68 | * [8.3 调整 evconnlistener 的回调函数](83_diao_zheng_evconnlistener_de_hui_diao_han_shu.md) 69 | * [8.4 检测 evconnlistener](84_jian_ce_evconnlistener.md) 70 | * [8.5 侦测错误](85_zhen_ce_cuo_wu.md) 71 | * [9 libevent常用设置](4-libevent简单服务器.md) 72 | * [9.1 日志消息回调设置](41_ri_zhi_xiao_xi_hui_diao_she_zhi.md) 73 | * [9.2 致命错误回调设置](42_zhi_ming_cuo_wu_hui_diao_she_zhi.md) 74 | * [9.3 内存管理回调设置](43_nei_cun_guan_li_hui_diao_she_zhi.md) 75 | * [9.4 锁和线程的设置](43_suo_he_xian_cheng_de_she_zhi.md) 76 | * [9.5 调试事件的使用](45_diao_shi_shi_jian_de_shi_yong.md) 77 | * [10 基于libevent服务器](10_ji_yu_libevent_fu_wu_qi.md) 78 | * [10.1 Hello_World服务器(基于信号)](101_helloworld_fu_wu_qi.md) 79 | * [10.2 基于事件服务器](102_ji_yu_shi_jian_fu_wu_qi.md) 80 | * [10.3 回显服务器](102_hui_xian_fu_wu_qi.md) 81 | * [10.3 libevent实现http服务器](103_libeventshi_xian_http_fu_wu_qi.md) 82 | * 10.4 libevent实现TCP/IP服务器 83 | 84 | --- 85 | ### About the author 86 | 87 | `name`:`Aceld(刘丹冰)` 88 | 89 | `mail`: 90 | [danbing.at@gmail.com](mailto:danbing.at@gmail.com) 91 | 92 | `github`: 93 | [https://github.com/aceld](https://github.com/aceld) 94 | 95 | `原创技术文章作品`: 96 | [https://www.yuque.com/aceld](https://www.yuque.com/aceld) 97 | 98 | ### 技术讨论社区 99 | | **WeChat** | **WeChat Public Account** | **QQ Group** | 100 | | ---- | ---- | ---- | 101 | | | | | 102 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Libevent深入浅出](README.md) 4 | * [1 Libevent官方](chapter1.md) 5 | * [2 epoll](2-epoll.md) 6 | * [2.1 流-IO操作-阻塞](21-流-io.md) 7 | * [2.2 解决阻塞死等待的办法](21-解决阻塞死等待的办法.md) 8 | * [2.3 什么是epoll](23-什么是epoll.md) 9 | * [2.4 epollAPI](24-epollapi.md) 10 | * [2.5 触发模式](25hong_fa_mo_5f0f_md.md) 11 | * [2.6 简单的epoll服务器](26-简单的epoll服务器.md) 12 | * [3 epoll和reactor](3-epoll和reactor.md) 13 | * [3.1 reactor反应堆模式](31_reactorfan_ying_dui_mo_shi.md) 14 | * [3.2 epoll的反应堆模式实现](32_epollde_fan_ying_dui_mo_shi_shi_xian.md) 15 | * [4 event_base](5-libevent编程api.md) 16 | * [4.1 创建event_base](41_jian_li_mo_ren_de_event_base.md) 17 | * [4.2 检查event_base后端](42_jian_cha_event_base_hou_duan.md) 18 | * [4.3 释放event_base](43_shi_fang_event_base.md) 19 | * [4.4 event_base优先级](44_eventbase_you_xian_ji.md) 20 | * [4.5 event_base和fork](45_eventbase_he_fork.md) 21 | * [5 事件循环event_loop](5_eventloop_shi_jian_xun_huan.md) 22 | * [5.1 运行循环](51_yun_xing_xun_huan.md) 23 | * [5.2 停止循环](52_ting_zhi_xun_huan.md) 24 | * [5.3 转储event_base的状态](53_zhuan_chu_event_base_de_zhuang_tai.md) 25 | * [6 事件event](6_shi_jian.md) 26 | * [6.1 创建事件](61_chuang_jian_shi_jian.md) 27 | * [6.2 事件的未决和非未决](62_shi_jian_de_wei_jue_he_fei_wei_jue.md) 28 | * [6.3 事件的优先级](63_shi_jian_de_you_xian_ji.md) 29 | * [6.4 检查事件状态](64_jian_cha_shi_jian_zhuang_tai.md) 30 | * [6.5 一次触发事件](65_yi_ci_hong_fa_shi_jian.md) 31 | * [6.6 手动激活事件](66_shou_dong_ji_huo_shi_jian.md) 32 | * [6.7 事件状态之间的转换](67_shi_jian_zhuang_tai_zhi_jian_de_zhuan_huan.md) 33 | * [7 数据缓冲Bufferevent](7_bufferevent.md) 34 | * [7.1 回调和水位](71_hui_diao_he_shui_wei.md) 35 | * [7.2 延迟回调](72_yan_chi_hui_diao.md) 36 | * [7.3 bufferevent 选项标志](73_bufferevent_xuan_xiang_biao_zhi.md) 37 | * [7.4 使用bufferevent](74_shi_yong_bufferevent.md) 38 | * [7.5 通用bufferevent操作](75_tong_yong_bufferevent_cao_zuo.md) 39 | * [7.5.1 释放bufferevent操作](751_shi_fang_bufferevent_cao_zuo.md) 40 | * [7.5.2 操作回调、水位和启用/禁用](752_cao_zuo_hui_diao_3001_shui_wei_he_qi_7528_jin_.md) 41 | * [7.5.3 操作bufferevent中的数据](753_cao_zuo_bufferevent_zhong_de_shu_ju.md) 42 | * [7.5.4 bufferevent的清空操作](755_buffereventde_qing_kong_cao_zuo.md) 43 | * [8 数据封装evBuffer](8_evbuffer.md) 44 | * [8.1 创建和释放evbuffer](81_chuang_jian_he_shi_fang_evbuffer.md) 45 | * [8.2 evbuffer与线程安全](82_evbufferyu_xian_cheng_an_quan.md) 46 | * [8.3 检查evbuffer](83_jian_cha_evbuffer.md) 47 | * [8.4 向evbuffer添加数据](84_xiang_evbuffer_tian_jia_shu_ju.md) 48 | * [8.5 evbuffer数据移动](85_evbuffershu_ju_yi_dong.md) 49 | * [8.6 添加数据到evbuffer前](86_tian_jia_shu_ju_dao_evbuffer_qian.md) 50 | * [8 链接监听器evconnlistener](8_lian_jie_jian_ting_qi_evconnlistener.md) 51 | * [8.1 创建和释放 evconnlistener](81_chuang_jianhe_shi_fang_evconnlistener.md) 52 | * [8.2 启用和禁用 evconnlistener](82_qi_yong_he_jin_yong_evconnlistener.md) 53 | * [8.3 调整 evconnlistener 的回调函数](83_diao_zheng_evconnlistener_de_hui_diao_han_shu.md) 54 | * [8.4 检测 evconnlistener](84_jian_ce_evconnlistener.md) 55 | * [8.5 侦测错误](85_zhen_ce_cuo_wu.md) 56 | * [9 libevent常用设置](4-libevent简单服务器.md) 57 | * [9.1 日志消息回调设置](41_ri_zhi_xiao_xi_hui_diao_she_zhi.md) 58 | * [9.2 致命错误回调设置](42_zhi_ming_cuo_wu_hui_diao_she_zhi.md) 59 | * [9.3 内存管理回调设置](43_nei_cun_guan_li_hui_diao_she_zhi.md) 60 | * [9.4 锁和线程的设置](43_suo_he_xian_cheng_de_she_zhi.md) 61 | * [9.5 调试事件的使用](45_diao_shi_shi_jian_de_shi_yong.md) 62 | * [10 基于libevent服务器](10_ji_yu_libevent_fu_wu_qi.md) 63 | * [10.1 Hello_World服务器(基于信号)](101_helloworld_fu_wu_qi.md) 64 | * [10.2 基于事件服务器](102_ji_yu_shi_jian_fu_wu_qi.md) 65 | * [10.3 回显服务器](102_hui_xian_fu_wu_qi.md) 66 | * [10.3 libevent实现http服务器](103_libeventshi_xian_http_fu_wu_qi.md) 67 | * 10.4 libevent实现TCP/IP服务器 68 | 69 | -------------------------------------------------------------------------------- /assets/libevent-2-epoll-api01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent-2-epoll-api01.png -------------------------------------------------------------------------------- /assets/libevent-2-epoll-api02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent-2-epoll-api02.png -------------------------------------------------------------------------------- /assets/libevent-2-epoll-api03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent-2-epoll-api03.png -------------------------------------------------------------------------------- /assets/libevent-2-io操作01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent-2-io操作01.png -------------------------------------------------------------------------------- /assets/libevent-2-io操作02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent-2-io操作02.png -------------------------------------------------------------------------------- /assets/libevent-2-解决阻塞01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent-2-解决阻塞01.png -------------------------------------------------------------------------------- /assets/libevent-2-解决阻塞02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent-2-解决阻塞02.png -------------------------------------------------------------------------------- /assets/libevent-2-阻塞01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent-2-阻塞01.png -------------------------------------------------------------------------------- /assets/libevent-2-阻塞02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent-2-阻塞02.png -------------------------------------------------------------------------------- /assets/libevent深入浅出封面.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/assets/libevent深入浅出封面.jpg -------------------------------------------------------------------------------- /chapter1.md: -------------------------------------------------------------------------------- 1 | # 1 Libevent官方 2 | 3 | * 官方网站:http://libevent.org/ 4 | 5 | libevent版本一共有1.4系列和2.0系列两个稳定版本。 6 | 7 | 8 | 9 | >1.4系列比较古老,但是源码简单,适合源码的学习 10 | 11 | >2.0系列比较新,建议直接使用2.0 12 | 13 | 需要注意的是,1.4系列和2.0系列两个版本的接口并不兼容,就是2.0将一些接口的原型发>生了改变,所以将1.4升级到2.0需要重新编码。 14 | 15 | #1.1 libevent 特点 16 | 17 | * 事件驱动,高性能; 18 | * 轻量级,专注于网络; 19 | * 跨平台,支持 Windows、Linux、Mac Os等; 20 | * 支持多种 I/O多路复用技术, epoll、poll、dev/poll、select 和kqueue 等; 21 | * 支持 I/O,定时器和信号等事件; 22 | 23 | 24 | #1.2 libevent下载与安装 25 | 26 | 27 | 在官网上找到`libevent-2.0.22-stable.tar.gz`下载地址。 28 | 29 | ```bash 30 | 31 | tar -zxvf libevent-2.0.22-stable.tar.gz 32 | 33 | cd libevent-2.0.22-stable/ 34 | 35 | ./configure 36 | 37 | make 38 | 39 | sudo make install 40 | ``` 41 | 42 | >注意 43 | > 44 | 如果在libevent安装目录make之后会生成一个.libs/, 里面如果没有libevent_openssl.so说明系统没有安装openssl库。 45 | 但是如果安装了,依然没有这个文件生成,可能需要制定openssl路径 46 | 47 | ```bash 48 | ln -s /usr/local/ssl/include/openssl /usr/include/openssl 49 | ``` 50 | 51 | 52 | #1.3 libevent开源包 53 | 54 | 在`.libs`隐藏文件中包含全部libevent已经编译好的so文件。 55 | 56 | 其中core为libevent的核心文件,libevent.so为主链接文件,会关联到其他全部so文件。 57 | 58 | 59 | 在sample目录下会有已经编译好的服务器应用程序。 60 | 61 | 可以拿`hello-world`程序用来测试。 62 | 63 | 服务端: 64 | 65 | ```bash 66 | ./hello-world 67 | ``` 68 | 69 | 客户端: 70 | 71 | ```bash 72 | netcat 192.168.2.105 9995 73 | ``` 74 | 75 | 如果客户端收到“hello world”字符串,表示libevent在本机可以正常使用。 76 | -------------------------------------------------------------------------------- /libevent-2-epoll触发模式01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/libevent-2-epoll触发模式01.png -------------------------------------------------------------------------------- /libevent-2-epoll触发模式02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/libevent-2-epoll触发模式02.png -------------------------------------------------------------------------------- /libevent-2-epoll触发模式03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/libevent-2-epoll触发模式03.png -------------------------------------------------------------------------------- /libevent-2-epoll触发模式04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/libevent-2-epoll触发模式04.png -------------------------------------------------------------------------------- /libevent-2-解决阻塞03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/libevent-2-解决阻塞03.png -------------------------------------------------------------------------------- /libevent-2-解决阻塞04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/libevent-2-解决阻塞04.png -------------------------------------------------------------------------------- /libevent-3-反应堆模式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/libevent-3-反应堆模式.png -------------------------------------------------------------------------------- /libevent-3-反应堆模式02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/libevent-3-反应堆模式02.png -------------------------------------------------------------------------------- /libevent-6-事件状态图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aceld/libevent/7ece7251c4c3624ce31872e10fef697a16d5e88f/libevent-6-事件状态图.png --------------------------------------------------------------------------------