Libevent学习之处理可读 and 可写事件
来源:互联网 发布:suse12 linux 网卡配置 编辑:程序博客网 时间:2024/05/24 07:33
处理可读事件:
底层fd接收到数据以后,bufferevent是如何工作的。
通过函数bufferevent_setwatermask可以设置读和写的水位.
低水位比较容易懂,就是当可读的数据量到达这个低水位后,才会调用用户设置的回调函数。比如用户想每次读取100字节,那么就可以把低水位设置为100。当可读数据的字节数小于100时,即使有数据都不会打扰用户(即不会调用用户设置的回调函数)。可读数据大于等于100字节后,才会调用用户的回调函数.
高水位:把读事件的evbuffer的数据量限制在高水位之下,类似于限速限流量功能.
如果用户设置了读事件的高水位,那么当读缓冲区的数据量达到这个高水位时,使用函数bufferevent_wm_suspend_read把监听读事件的event挂起来,暂时不读取socket fd中的数据.
下面看一下Libevent是怎么把一个event挂起来的
bufferevent_readcb(evutil_socket_t fd, short event, void *arg){... /* * If we have a high watermark configured then we don't want to * read more data than would make us reach the watermark. */ if (bufev->wm_read.high != 0) { //当前读buffer比高水位要高 howmuch = bufev->wm_read.high - evbuffer_get_length(input); /* we somehow lowered the watermark, stop reading */ if (howmuch <= 0) { bufferevent_wm_suspend_read(bufev); goto done; } } readmax = _bufferevent_get_read_max(bufev_p);...}
当前读缓冲区大小大于设置的高水位时,会调用函数将读事件挂起
//bufferevent-internal.h文件 #define bufferevent_wm_suspend_read(b) \ bufferevent_suspend_read((b), BEV_SUSPEND_WM) //bufferevent.c文件 void bufferevent_suspend_read(struct bufferevent *bufev, bufferevent_suspend_flags what) { struct bufferevent_private *bufev_private = EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); BEV_LOCK(bufev); if (!bufev_private->read_suspended)//不能挂多次 bufev->be_ops->disable(bufev, EV_READ);//实际调用be_socket_disable函数 bufev_private->read_suspended |= what;//记录被挂起原因 BEV_UNLOCK(bufev); } //bufferevent_sock.c文件 static int be_socket_disable(struct bufferevent *bufev, short event) { struct bufferevent_private *bufev_p = EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); if (event & EV_READ) { if (event_del(&bufev->ev_read) == -1) return -1; } /* Don't actually disable the write if we are trying to connect. */ if ((event & EV_WRITE) && ! bufev_p->connecting) { if (event_del(&bufev->ev_write) == -1)//删掉这个event return -1; } return 0; }
居然是直接删除了监听读事件的event!看来不能随便设置高水位,因为会导致暂停读.
那么什么时候取消挂起读事件呢,让bufferevent可以继续读取socket fd的数据呢?当移除evbuffer中的数据时,Libevent会检查这个evbuffer的数据是否会小于高水位,如果小于的话,那么就恢复读事件,也就是说evbuffer在添加或删除数据时,应该会有一个回调函数.
bufferevent_setwatermark不仅为高水位设置回调函数,还会检查当前evbuffer的数据量是否超过了高水位。因为这个设置水位函数可能是在bufferevent工作一段时间后才添加的,所以evbuffer是有可能已经有数据的了,因此需要检查。如果超过了水位值,那么就需要挂起读。当然也存在另外一种可能:用户之前设置过了一个比较大的高水位,挂起了读。现在发现错了,就把高水位调低一点,此时就需要恢复读。
现在假设用户移除了一些evbuffer的数据,进而触发了evbuffer的回调函数,当然也就调用了函数bufferevent_inbuf_wm_cb。下面看一下这个函数是怎么恢复读的。
//bufferevent.c文件 static void bufferevent_inbuf_wm_cb(struct evbuffer *buf, const struct evbuffer_cb_info *cbinfo, void *arg) { struct bufferevent *bufev = arg; size_t size; size = evbuffer_get_length(buf); if (size >= bufev->wm_read.high) bufferevent_wm_suspend_read(bufev); else bufferevent_wm_unsuspend_read(bufev); } //bufferevent-internal.h文件 #define bufferevent_wm_unsuspend_read(b) \ bufferevent_unsuspend_read((b), BEV_SUSPEND_WM) //bufferevent.c文件 void bufferevent_unsuspend_read(struct bufferevent *bufev, bufferevent_suspend_flags what) { struct bufferevent_private *bufev_private = EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); BEV_LOCK(bufev); bufev_private->read_suspended &= ~what; if (!bufev_private->read_suspended && (bufev->enabled & EV_READ)) bufev->be_ops->enable(bufev, EV_READ);//重新把event插入到event_base中 BEV_UNLOCK(bufev); }
因为用户可以手动为这个evbuffer添加数据,此时也会调用bufferevent_inbuf_wm_cb函数。此时就要检查evbuffer的数据量是否已经超过高水位了,而不能仅仅检查是否低于高水位。
从socket中读取数据
从前面的一系列博文可以知道,如果一个socket可读了,那么监听可读事件的event的回调函数就会被调用。这个回调函数是在bufferevent_socket_new函数中被Libevent内部设置的,设置为bufferevent_readcb函数,用户并不知情。
当socket有数据可读时,Libevent就会监听到,然后调用bufferevent_readcb函数处理。该函数会调用evbuffer_read函数,把数据从socket fd中读取到evbuffer中。然后再调用用户在bufferevent_setcb函数中设置的读事件回调函数。所以,当用户的读事件回调函数被调用时,数据已经在evbuffer中了,用户拿来就用,无需调用read这类会阻塞的函数。
处理写事件
如果一个event监听了可写事件,那么这个event就会一直被触发(死循环)。因为一般情况下,如果不是发大量的数据这个写缓冲区是不会满的。不能监听可写事件。但我们确实要往fd中写数据,那怎么办?Libevent的做法是:当我们确实要写入数据时,才监听可写事件。也就是说我们调用bufferevent_write写入数据时,Libevent才会把监听可写事件的那个event注册到event_base中。当Libevent把数据都写入到fd的缓冲区后,Libevent又会把这个event从event_base中删除。比较烦琐.
同前面的监听可读一样,Libevent是在bufferevent_socket_new函数设置可写的回调函数,为bufferevent_writecb。
//bufferevent_sock.c文件 static void bufferevent_writecb(evutil_socket_t fd, short event, void *arg) { struct bufferevent *bufev = arg; struct bufferevent_private *bufev_p = EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); int res = 0; short what = BEV_EVENT_WRITING; int connected = 0; ev_ssize_t atmost = -1; _bufferevent_incref_and_lock(bufev); if (event == EV_TIMEOUT) { /* Note that we only check for event==EV_TIMEOUT. If * event==EV_TIMEOUT|EV_WRITE, we can safely ignore the * timeout, since a read has occurred */ what |= BEV_EVENT_TIMEOUT; goto error; } ...//判断这个socket是否已经连接上服务器了 //用户可能设置了限速,如果没有限速,那么atmost将返回16384(16K) atmost = _bufferevent_get_write_max(bufev_p); //一些原因导致写被挂起来了 if (bufev_p->write_suspended) goto done; //如果evbuffer有数据可以写到sockfd中 if (evbuffer_get_length(bufev->output)) { //解冻链表头 evbuffer_unfreeze(bufev->output, 1); //将output这个evbuffer的数据写到socket fd 的缓冲区中 //会把已经写到socket fd缓冲区的数据,从evbuffer中删除 res = evbuffer_write_atmost(bufev->output, fd, atmost); evbuffer_freeze(bufev->output, 1); if (res == -1) { int err = evutil_socket_geterror(fd); if (EVUTIL_ERR_RW_RETRIABLE(err))//可以恢复的错误。一般是EINTR或者EAGAIN goto reschedule; what |= BEV_EVENT_ERROR; } else if (res == 0) {//该socket已经断开连接了 what |= BEV_EVENT_EOF; } if (res <= 0) goto error; } //如果把写缓冲区的数据都写完成了。为了防止event_base不断地触发可写 //事件,此时要把这个监听可写的event删除。 //前面的atmost限制了一次最大的可写数据。如果还没写所有的数据 //那么就不能delete这个event,而是要继续监听可写事情,知道把所有的 //数据都写到socket fd中。 if (evbuffer_get_length(bufev->output) == 0) { event_del(&bufev->ev_write); } //如果evbuffer里面的数据量已经写得七七八八了,小于设置的低水位值,那么 //就会调用用户设置的写事件回调函数 if ((res || !connected) && evbuffer_get_length(bufev->output) <= bufev->wm_write.low) { _bufferevent_run_writecb(bufev); } goto done; reschedule: if (evbuffer_get_length(bufev->output) == 0) { event_del(&bufev->ev_write); } goto done; error: bufferevent_disable(bufev, EV_WRITE);//有错误。把这个写event删除 _bufferevent_run_eventcb(bufev, what); done: _bufferevent_decref_and_unlock(bufev); }
- Libevent学习之处理可读 and 可写事件
- socket可读/可写
- Libevent之事件处理框架-event_base结构体学习
- 网络编程学习笔记--1.socket可读可写条件
- 网络编程学习笔记--socket可读可写条件
- 网络编程学习笔记--1.socket可读可写条件
- socket 可读 可写 条件
- socket 可读 可写 条件
- socket可读可写条件
- Libevent学习-----Reactor的事件处理机制
- TextBox禁止输入 后台可读可写
- 修改android文件系统为可读可写
- 修改android文件系统为可读可写
- socket可读,可写的条件
- access测试文件是否可读/可写
- 唯快不破:socket 可读 可写 条件
- socket可读,可写的条件
- Libevent学习----信号事件
- Android卡片布局的两种实现方式
- 线段树
- win7guest账户开启方法
- 历届试题 回文数字
- Consul,在路上
- Libevent学习之处理可读 and 可写事件
- 【数据结构】-线性表-链表 熟练度max=4(split)
- mybaits错误解决:There is no getter for property named 'id' in class 'java.lang.String'
- 彻底理解java语言的线程安全volatile用法
- c指针学习
- target runtime apache v7.0 not defined
- (4)Java多线程之安全问题-下
- 15位和18位身份证JS校验实例(jquery)和注意事项
- 文章标题