开源的网络事件库有很多种,大家最熟悉的莫过于集成在nginx里的event模块,很灵巧也很好的支撑了nginx的高性能服务。但是分析源码后,大家其实都比较类似,底层采用epoll机制探测事件,从而驱动上层业务运行。libuv也是一个开源的网络事件库,它的设计也比较巧妙,很轻量级。
mediasoup中,libuv使用由以下两个函数启动,ClassInit负责生成uv_loop_t对象,并初始化相关对象,类似nginx的ngx_worker_process_cycle,RunLoop调用uv_run,启动循环,不断通过epoll探测事件发生,驱动业务运行。
void DepLibUV::ClassInit()
{
// NOTE: Logger depends on this so we cannot log anything here.
DepLibUV::loop = new uv_loop_t;
int err = uv_loop_init(DepLibUV::loop);
if (err != 0)
MS_ABORT("libuv initialization failed");
}
void DepLibUV::RunLoop()
{
MS_TRACE();
// This should never happen.
MS_ASSERT(DepLibUV::loop != nullptr, "loop unset");
uv_run(DepLibUV::loop, UV_RUN_DEFAULT);
}
下面的调用栈详细描述了基于libuv事件驱动的调用栈,当uv__io_poll发现有epoll事件发生时,调用注册的回调函数将数据一层层传递到业务层RTC::WebRtcTransport::OnPacketReceived进行处理。
下面以WebRtcTransport的Udp接收端口的初始化为例,来描述一下libuv回调事件初始化流程。创建WebRtcTransport对象时,同时创建本地UdpSocket,初始化uv_udp_t对象,执行uv_udp_recv_start给socket事件设置回调函数,从而完成了初始化过程。当有数据到达时,根据回调函数,依次执行到业务层。
WebRtcTransport::WebRtcTransport
{
...
auto* udpSocket = new RTC::UdpSocket(this, listenIp.ip);
...
}
UdpSocket::UdpSocket(uv_udp_t* uvHandle) : uvHandle(uvHandle)
{
...
err = uv_udp_recv_start(
this->uvHandle, static_cast<uv_alloc_cb>(onAlloc), static_cast<uv_udp_recv_cb>(onRecv));
...
}