struct bufferevent * bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options) { struct bufferevent_private *bufev_p; struct bufferevent *bufev; #ifdef _WIN32 if (base && event_base_get_iocp_(base)) return bufferevent_async_new_(base, fd, options); #endif if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL) return NULL; if (bufferevent_init_common_(bufev_p, base, &bufferevent_ops_socket, options) < 0) { mm_free(bufev_p); return NULL; } bufev = &bufev_p->bev; evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD); event_assign(&bufev->ev_read, bufev->ev_base, fd, EV_READ|EV_PERSIST, bufferevent_readcb, bufev); event_assign(&bufev->ev_write, bufev->ev_base, fd, EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev); evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev); evbuffer_freeze(bufev->input, 0); evbuffer_freeze(bufev->output, 1); return bufev; }
struct bufferevent * bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options) { struct bufferevent_private *bufev_p; struct bufferevent *bufev; #ifdef WIN32 if (base && event_base_get_iocp(base)) return bufferevent_async_new(base, fd, options); #endif if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL) return NULL; if (bufferevent_init_common(bufev_p, base, &bufferevent_ops_socket, options) < 0) { mm_free(bufev_p); return NULL; } bufev = &bufev_p->bev; //设置将evbuffer的数据向fd传 evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD); //设置读写回调 event_assign(&bufev->ev_read, bufev->ev_base, fd, EV_READ|EV_PERSIST, bufferevent_readcb, bufev); event_assign(&bufev->ev_write, bufev->ev_base, fd, EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev); //设置evbuffer的回调函数,使得外界给写缓冲区添加数据时,能触发 //写操作,这个回调对于写事件的监听是很重要的 evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev); /* 虽然这里冻结了,但实际上Libevent在读数据或者写数据之前会解冻的读完或者写完数据后,又会马上冻结。 * 这主要防止数据被意外修改。用户一般不会直接调用evbuffer_freeze或者evbuffer_unfreeze函数。 * 一切的冻结和解冻操作都由Libevent内部完成。还有一点要注意,因为这里只是把写缓冲区的头部冻结了。 * 所以还是可以往写缓冲区的尾部追加数据。同样,此时也是可以从读缓冲区读取数据。这个是必须的。 * 因为在Libevent内部不解冻的时候,用户需要从读缓冲区中获取数据(这相当于从socket fd中读取数据), * 用户也需要把数据写到写缓冲区中(这相当于把数据写入到socket fd中)。*/ //冻结读缓冲区的尾部,未解冻之前不能往读缓冲区追加数据 //也就是说不能从socket fd中读取数据 evbuffer_freeze(bufev->input, 0); //冻结写缓冲区的头部,未解冻之前不能把写缓冲区的头部数据删除 //也就是说不能把数据写到socket fd evbuffer_freeze(bufev->output, 1); return bufev; }
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; } if (bufev_p->connecting) { int c = evutil_socket_finished_connecting_(fd); /* we need to fake the error if the connection was refused * immediately - usually connection to localhost on BSD */ if (bufev_p->connection_refused) { bufev_p->connection_refused = 0; c = -1; } if (c == 0) goto done; bufev_p->connecting = 0; if (c < 0) { event_del(&bufev->ev_write); event_del(&bufev->ev_read); bufferevent_run_eventcb_(bufev, BEV_EVENT_ERROR); goto done; } else { connected = 1; #ifdef _WIN32 if (BEV_IS_ASYNC(bufev)) { event_del(&bufev->ev_write); bufferevent_async_set_connected_(bufev); bufferevent_run_eventcb_(bufev, BEV_EVENT_CONNECTED); goto done; } #endif bufferevent_run_eventcb_(bufev, BEV_EVENT_CONNECTED); if (!(bufev->enabled & EV_WRITE) || bufev_p->write_suspended) { event_del(&bufev->ev_write); goto done; } } } atmost = bufferevent_get_write_max_(bufev_p); if (bufev_p->write_suspended) goto done; if (evbuffer_get_length(bufev->output)) { evbuffer_unfreeze(bufev->output, 1); 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)) goto reschedule; what |= BEV_EVENT_ERROR; } else if (res == 0) { /* eof case XXXX Actually, a 0 on write doesn't indicate an EOF. An ECONNRESET might be more typical. */ what |= BEV_EVENT_EOF; } if (res <= 0) goto error; bufferevent_decrement_write_buckets_(bufev_p, res); } if (evbuffer_get_length(bufev->output) == 0) { event_del(&bufev->ev_write); } /* * Invoke the user callback if our buffer is drained or below the * low watermark. */ 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); bufferevent_run_eventcb_(bufev, what); done: bufferevent_decref_and_unlock_(bufev); }
static void bufferevent_readcb(evutil_socket_t fd, short event, void *arg) { struct bufferevent *bufev = arg; struct bufferevent_private *bufev_p = EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); struct evbuffer *input; int res = 0; short what = BEV_EVENT_READING; ev_ssize_t howmuch = -1, readmax=-1; bufferevent_incref_and_lock_(bufev); if (event == EV_TIMEOUT) { /* Note that we only check for event==EV_TIMEOUT. If * event==EV_TIMEOUT|EV_READ, we can safely ignore the * timeout, since a read has occurred */ what |= BEV_EVENT_TIMEOUT; goto error; } input = bufev->input; /* * 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) { 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); if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited" * uglifies this code. XXXX */ howmuch = readmax; if (bufev_p->read_suspended) goto done; evbuffer_unfreeze(input, 0); res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */ evbuffer_freeze(input, 0); if (res == -1) { int err = evutil_socket_geterror(fd); if (EVUTIL_ERR_RW_RETRIABLE(err)) goto reschedule; /* error case */ what |= BEV_EVENT_ERROR; } else if (res == 0) { /* eof case */ what |= BEV_EVENT_EOF; } if (res <= 0) goto error; bufferevent_decrement_read_buckets_(bufev_p, res); /* Invoke the user callback - must always be called last */ if (evbuffer_get_length(input) >= bufev->wm_read.low) bufferevent_run_readcb_(bufev); goto done; reschedule: goto done; error: bufferevent_disable(bufev, EV_READ); bufferevent_run_eventcb_(bufev, what); done: bufferevent_decref_and_unlock_(bufev); }
int evbuffer_launch_read(struct evbuffer *buf, size_t at_most, struct event_overlapped *ol) { struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); int r = -1, i; int nvecs; int npin=0; struct evbuffer_chain *chain=NULL, **chainp; DWORD bytesRead; DWORD flags = 0; struct evbuffer_iovec vecs[MAX_WSABUFS]; if (!buf_o) return -1; EVBUFFER_LOCK(buf); EVUTIL_ASSERT(!buf_o->write_in_progress); if (buf->freeze_end || buf_o->read_in_progress) goto done; buf_o->first_pinned = NULL; buf_o->n_buffers = 0; memset(buf_o->buffers, 0, sizeof(buf_o->buffers)); if (_evbuffer_expand_fast(buf, at_most, MAX_WSABUFS) == -1) goto done; evbuffer_freeze(buf, 0); nvecs = _evbuffer_read_setup_vecs(buf, at_most, vecs, MAX_WSABUFS, &chainp, 1); for (i=0;i<nvecs;++i) { WSABUF_FROM_EVBUFFER_IOV( &buf_o->buffers[i], &vecs[i]); } buf_o->n_buffers = nvecs; buf_o->first_pinned = chain = *chainp; npin=0; for ( ; chain; chain = chain->next) { _evbuffer_chain_pin(chain, EVBUFFER_MEM_PINNED_R); ++npin; } EVUTIL_ASSERT(npin == nvecs); _evbuffer_incref(buf); if (WSARecv(buf_o->fd, buf_o->buffers, nvecs, &bytesRead, &flags, &ol->overlapped, NULL)) { int error = WSAGetLastError(); if (error != WSA_IO_PENDING) { /* An actual error. */ pin_release(buf_o, EVBUFFER_MEM_PINNED_R); evbuffer_unfreeze(buf, 0); evbuffer_free(buf); /* decref */ goto done; } } buf_o->read_in_progress = 1; r = 0; done: EVBUFFER_UNLOCK(buf); return r; }
int evbuffer_launch_write(struct evbuffer *buf, ev_ssize_t at_most, struct event_overlapped *ol) { struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); int r = -1; int i; struct evbuffer_chain *chain; DWORD bytesSent; if (!buf) { /* No buffer, or it isn't overlapped */ return -1; } EVBUFFER_LOCK(buf); EVUTIL_ASSERT(!buf_o->read_in_progress); if (buf->freeze_start || buf_o->write_in_progress) goto done; if (!buf->total_len) { /* Nothing to write */ r = 0; goto done; } else if (at_most < 0 || (size_t)at_most > buf->total_len) { at_most = buf->total_len; } evbuffer_freeze(buf, 1); buf_o->first_pinned = NULL; buf_o->n_buffers = 0; memset(buf_o->buffers, 0, sizeof(buf_o->buffers)); chain = buf_o->first_pinned = buf->first; for (i=0; i < MAX_WSABUFS && chain; ++i, chain=chain->next) { WSABUF *b = &buf_o->buffers[i]; b->buf = (char*)( chain->buffer + chain->misalign ); _evbuffer_chain_pin(chain, EVBUFFER_MEM_PINNED_W); if ((size_t)at_most > chain->off) { /* XXXX Cast is safe for now, since win32 has no mmaped chains. But later, we need to have this add more WSAbufs if chain->off is greater than ULONG_MAX */ b->len = (unsigned long)chain->off; at_most -= chain->off; } else { b->len = (unsigned long)at_most; ++i; break; } } buf_o->n_buffers = i; _evbuffer_incref(buf); if (WSASend(buf_o->fd, buf_o->buffers, i, &bytesSent, 0, &ol->overlapped, NULL)) { int error = WSAGetLastError(); if (error != WSA_IO_PENDING) { /* An actual error. */ pin_release(buf_o, EVBUFFER_MEM_PINNED_W); evbuffer_unfreeze(buf, 1); evbuffer_free(buf); /* decref */ goto done; } } buf_o->write_in_progress = 1; r = 0; done: EVBUFFER_UNLOCK(buf); return r; }
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; } //正在连接服务器... if (bufev_p->connecting) { int c = evutil_socket_finished_connecting(fd); /* we need to fake the error if the connection was refused * immediately - usually connection to localhost on BSD */ if (bufev_p->connection_refused) { //在bufferevent_socket_connect中被设置 bufev_p->connection_refused = 0; c = -1; } if (c == 0) //正在连接,继续监听可写。 goto done; bufev_p->connecting = 0; if (c < 0) { //连接发生错误 event_del(&bufev->ev_write); event_del(&bufev->ev_read); _bufferevent_run_eventcb(bufev, BEV_EVENT_ERROR); goto done; } else { //连接成功 connected = 1; #ifdef WIN32 if (BEV_IS_ASYNC(bufev)) { event_del(&bufev->ev_write); bufferevent_async_set_connected(bufev); _bufferevent_run_eventcb(bufev, BEV_EVENT_CONNECTED); goto done; } #endif //连接成功,调用用户错误处理函数。比较奇怪。 _bufferevent_run_eventcb(bufev, BEV_EVENT_CONNECTED); if (!(bufev->enabled & EV_WRITE) || bufev_p->write_suspended) { event_del(&bufev->ev_write); goto done; } } } atmost = _bufferevent_get_write_max(bufev_p); //写被挂起 if (bufev_p->write_suspended) goto done; //存在可写数据 if (evbuffer_get_length(bufev->output)) { evbuffer_unfreeze(bufev->output, 1); //将缓冲区数据写入socket。 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)) //socket缓冲区满,一次未写完,继续监听可写事件,等待下次写入。 goto reschedule; what |= BEV_EVENT_ERROR; //写发生异常错误 } else if (res == 0) { /* eof case XXXX Actually, a 0 on write doesn't indicate an EOF. An ECONNRESET might be more typical. */ what |= BEV_EVENT_EOF; } if (res <= 0) goto error; _bufferevent_decrement_write_buckets(bufev_p, res); } //缓冲区数据已写完,删除写事件。 if (evbuffer_get_length(bufev->output) == 0) { event_del(&bufev->ev_write); } /* 低于写低水位,调用用户回调。 */ 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); _bufferevent_run_eventcb(bufev, what); done: _bufferevent_decref_and_unlock(bufev); }
static void bufferevent_readcb(evutil_socket_t fd, short event, void *arg) { struct bufferevent *bufev = arg; struct bufferevent_private *bufev_p = EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); struct evbuffer *input; int res = 0; short what = BEV_EVENT_READING; ev_ssize_t howmuch = -1, readmax=-1; _bufferevent_incref_and_lock(bufev); if (event == EV_TIMEOUT) { /* Note that we only check for event==EV_TIMEOUT. If * event==EV_TIMEOUT|EV_READ, we can safely ignore the * timeout, since a read has occurred */ what |= BEV_EVENT_TIMEOUT; goto error; } input = bufev->input; /* * 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) { howmuch = bufev->wm_read.high - evbuffer_get_length(input); /* 缓冲区超过高水位,挂起读。 */ if (howmuch <= 0) { bufferevent_wm_suspend_read(bufev); goto done; } } //因为用户可以限速,所以这么要检测最大的可读大小。 //如果没有限速的话,那么将返回16384字节,即16K //默认情况下是没有限速的。 readmax = _bufferevent_get_read_max(bufev_p); if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited" * uglifies this code. XXXX */ howmuch = readmax; if (bufev_p->read_suspended) goto done; evbuffer_unfreeze(input, 0); res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */ evbuffer_freeze(input, 0); if (res == -1) { int err = evutil_socket_geterror(fd); if (EVUTIL_ERR_RW_RETRIABLE(err)) //EINTER or EAGAIN goto reschedule; /* error case */ what |= BEV_EVENT_ERROR; } else if (res == 0) { /* eof case */ what |= BEV_EVENT_EOF; } if (res <= 0) goto error; _bufferevent_decrement_read_buckets(bufev_p, res); /* 数据大于低水平,调用用户设置的回调。 */ if (evbuffer_get_length(input) >= bufev->wm_read.low) _bufferevent_run_readcb(bufev); goto done; reschedule: goto done; error: bufferevent_disable(bufev, EV_READ); _bufferevent_run_eventcb(bufev, what); done: _bufferevent_decref_and_unlock(bufev); }