#协程中使用三方网络库
在协程中使用三方网络库有两种方式:
- 直接使用三方网络库的阻塞 API,此方式最简单,依赖于 co 内部的系统 API hook。
- 使用三方网络库的非阻塞 API,此方式需要借助 co::io_event 将其转换为同步方式。
#系统 API hook 原理
API hook 简单来说就是拦截系统 API 请求,如果该请求是在协程中,且使用 blocking socket,就将 socket 修改成 non-blocking 模式,当 socket 不可读或写时,利用 co::io_event
或 co 中更底层的接口等待 I/O 事件,I/O 事件到来时,再唤醒协程,调用系统原生的 socket API 完成 I/O 操作。
#使用非阻塞 API
下面是基于 openssl 的非阻塞 API 实现的 recv 方法:
int recv(S* s, void* buf, int n, int ms) {
CHECK(co::sched()) << "must be called in coroutine..";
int r, e;
int fd = SSL_get_fd((SSL*)s);
if (fd < 0) return -1;
do {
ERR_clear_error();
r = SSL_read((SSL*)s, buf, n);
if (r > 0) return r; // success
if (r == 0) return 0;
e = SSL_get_error((SSL*)s, r);
if (e == SSL_ERROR_WANT_READ) {
co::io_event ev(fd, co::ev_read);
if (!ev.wait(ms)) return -1;
} else if (e == SSL_ERROR_WANT_WRITE) {
co::io_event ev(fd, co::ev_write);
if (!ev.wait(ms)) return -1;
} else {
return r;
}
} while (true);
}
整个过程比较简单,底层使用 non-blocking socket,在 SSL_read
产生 SSL_ERROR_WANT_READ
错误时,用 co::io_event
等待读事件,产生 SSL_ERROR_WANT_WRITE
错误时,用 co::io_event
等待写事件,wait()
正常返回时,表示 socket 可读或可写,继续调用 SSL_read
完成 I/O 操作。
一般而言,提供非阻塞 I/O 接口的三方网络库,都可以用与上面类似的方法,将非阻塞 API 转换为同步方式。