bufferevent_writecb(evutil_socket_t fd, short event, void *arg)
{
struct bufferevent *bufev = arg;
struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
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, 0);
goto done;
} else {
connected = 1;
bufferevent_socket_set_conn_address_fd_(bufev, fd);
bufferevent_run_eventcb_(bufev,
BEV_EVENT_CONNECTED, 0);
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)) { // 获取buffer大小
evbuffer_unfreeze(bufev->output, 1);// 锁住buffer ,支持多线程
res = evbuffer_write_atmost(bufev->output, fd, atmost);// 写数据->send
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) {// 如果已经写完了,write len = 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) {
bufferevent_trigger_nolock_(bufev, EV_WRITE, 0);
}
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, 0);
done:
bufferevent_decref_and_unlock_(bufev);
}