libevent的接口兼容性做的还算不错,基本上替换一下就转到新版本了。但是,强制flush数据的时候出了问题。目前的应用场景是,遇到顶号登录这种情形,先用bufferevent_write向客户端发送错误信息,然后再断开socket。用的flush是这样的:
void try_flush(bufferevent *bev) { int size = evbuffer_get_length(bufferevent_get_output(bev)); if (size > 0) { evbuffer_write(bufferevent_get_output(bev), fd); } }
在1.4的时候,这段代码工作良好。但是,在2.0的libevent里,这段代码不能刷出数据。看了下errno和socket_errno,错误码是EINPROGRESS。百思不得其解下,在脚本层将关闭socket放在下一次libevent事件循环里完成,结果就好了。
大致猜测下,应该是libevent为了效率做的优化。因为write是比较昂贵的系统调用,若因为evbuffer_write的反复调用,而引起多次write,会影响性能。翻了一下libevent 2.0的代码,evbuffer_write是调用evbuffer_write_atmost的,对比下1.4 evbuffer_write和2.0的evbuffer_write_atmost:
int evbuffer_write(struct evbuffer *buffer, int fd) { int n; #ifndef WIN32 n = write(fd, buffer->buffer, buffer->off); #else n = send(fd, buffer->buffer, buffer->off, 0); #endif if (n == -1) return (-1); if (n == 0) return (0); evbuffer_drain(buffer, n); return (n); }
int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, ev_ssize_t howmuch) { int n = -1; EVBUFFER_LOCK(buffer); if (buffer->freeze_start) { goto done; } if (howmuch < 0 || (size_t)howmuch > buffer->total_len) howmuch = buffer->total_len; if (howmuch > 0) { #ifdef USE_SENDFILE struct evbuffer_chain *chain = buffer->first; if (chain != NULL && (chain->flags & EVBUFFER_SENDFILE)) n = evbuffer_write_sendfile(buffer, fd, howmuch); else { #endif #ifdef USE_IOVEC_IMPL n = evbuffer_write_iovec(buffer, fd, howmuch); #elif defined(WIN32) /* XXX(nickm) Don‘t disable this code until we know if * the WSARecv code above works. */ void *p = evbuffer_pullup(buffer, howmuch); n = send(fd, p, howmuch, 0); #else void *p = evbuffer_pullup(buffer, howmuch); n = write(fd, p, howmuch); #endif #ifdef USE_SENDFILE } #endif if (n > 0) evbuffer_drain(buffer, n); done: EVBUFFER_UNLOCK(buffer); return (n); }
2.0有个显眼的freeze_start的检查,正是这个检查,让evbuffer_write的调用直接跳过了发送阶段。于是,在flush的时候加了一个dirty trick,直接调用了evbuffer_unfreeze:
void try_flush(bufferevent *bev) { int size = evbuffer_get_length(bufferevent_get_output(bev)); if (size > 0) { evbuffer_unfreeze(bufferevent_get_output(bev), 1); evbuffer_write(bufferevent_get_output(bev), fd); } }
问题解决
libevent库1.4升级到2.0时无法flush的解决办法,布布扣,bubuko.com
libevent库1.4升级到2.0时无法flush的解决办法
原文:http://www.cnblogs.com/Lifehacker/p/libevent_upgrade_from_1-4_to_2-0-21.html