本文分析的srs版本是0.6.0
以下正在写作中。。。
srs源码分析7-create stream
srs源码分析8-推流-publish
srs源码分析9-推流-unpublish
srs源码分析10-拉流-play
srs源码分析11-拉流-pause
srs源码分析12-转发-forward
先从main函数开始分析,分析srs的启动过程。
int main(int argc, char** argv){
int ret = ERROR_SUCCESS;
/*注册SIGHUB信号处理函数,用于重载配置文件。*/
signal(SIGNAL_RELOAD, handler);
/*解析命令行参数,解析配置文件。*/
if ((ret = config->parse_options(argc, argv)) != ERROR_SUCCESS) {
return ret;
}
/*初始化st和日志*/
if ((ret = _server()->initialize()) != ERROR_SUCCESS) {
return ret;
}
/*开始监听客户端的连接*/
if ((ret = _server()->listen()) != ERROR_SUCCESS) {
return ret;
}
/*运行主协程*/
if ((ret = _server()->cycle()) != ERROR_SUCCESS) {
return ret;
}
return 0;
}
在main函数中做了如下几件事:
- 注册SIGHUB信号处理函数,用于在srs运行期间使用该信号重载配置文件。
- 解析命令行参数,解析配置文件。
- 初始化st库,设置日志id。
- 创建listen协程,用于监听客户端的连接。
- 运行主协程
SIGHUB信号的处理
#define SIGNAL_RELOAD SIGHUP
signal(SIGNAL_RELOAD, handler); /*注册信号处理函数*/
/*信号处理函数*/
void handler(int signo)
{
srs_trace("get a signal, signo=%d", signo);
_server()->on_signal(signo);
}
void SrsServer::on_signal(int signo)
{
if (signo == SIGNAL_RELOAD) {
signal_reload = true; /*将配置重载标志设置为true,表示需要重载配置文件。*/
}
}
在main函数的开始位置,会为SIGHUB
信号注册处理函数。当此信号触发时,会将signal_reload标志置为true。
初始化SrsServer
int SrsServer::initialize()
{
int ret = ERROR_SUCCESS;
/*给st设置epoll*/
if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) {
ret = ERROR_ST_SET_EPOLL;
srs_error("st_set_eventsys use linux epoll failed. ret=%d", ret);
return ret;
}
srs_verbose("st_set_eventsys use linux epoll success");
/*初始化st协程库*/
if(st_init() != 0){
ret = ERROR_ST_INITIALIZE;
srs_error("st_init failed. ret=%d", ret);
return ret;
}
srs_verbose("st_init success");
/*设置log id*/
log_context->generate_id();
srs_info("log set id success");
return ret;
}
在这个函数中,主要是初始化st库,在st_init()函数中会创建idle协程,当没有就绪协程时,会进入idle协程执行,同时会将主线程封装称为主协程,以便可以调度。
创建listen协程
int SrsServer::listen()
{
int ret = ERROR_SUCCESS;
SrsConfDirective* conf = NULL;
/*从配置文件中读取监听端口信息*/
conf = config->get_listen();
srs_assert(conf);
/*关闭之前的listener,在重载配置文件时才有用。*/
close_listeners();
/*创建监听器listener*/
for (int i = 0; i < (int)conf->args.size(); i++) {
SrsListener* listener = new SrsListener(this, SrsListenerStream);
listeners.push_back(listener);
/*获取监听端口号*/
int port = ::atoi(conf->args.at(i).c_str());
/*开始监听*/
if ((ret = listener->listen(port)) != ERROR_SUCCESS) {
srs_error("listen at port %d failed. ret=%d", port, ret);
return ret;
}
}
return ret;
}
srs的配置文件中默认监听的是1935端口号,srs可以监听多个端口号。
在srs启动的时候会调用SrsServer::listen()函数,在重载配置文件时,也会调用此函数。重载配置文件时,监听的端口号可能发生了变化,所以需要将之前的listener关闭,重新建立listener。
int SrsListener::listen(int _port)
{
int ret = ERROR_SUCCESS;
port = _port;
/*创建监听套接字*/
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
ret = ERROR_SOCKET_CREATE;
srs_error("create linux socket error. ret=%d", ret);
return ret;
}
srs_verbose("create linux socket success. fd=%d", fd);
/*设置套接字选项*/
int reuse_socket = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
ret = ERROR_SOCKET_SETREUSE;
srs_error("setsockopt reuse-addr error. ret=%d", ret);
return ret;
}
srs_verbose("setsockopt reuse-addr success. fd=%d", fd);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
/*绑定监听地址*/
if (bind(fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) {
ret = ERROR_SOCKET_BIND;
srs_error("bind socket error. ret=%d", ret);
return ret;
}
srs_verbose("bind socket success. fd=%d", fd);
if (::listen(fd, SERVER_LISTEN_BACKLOG) == -1) {
ret = ERROR_SOCKET_LISTEN;
srs_error("listen socket error. ret=%d", ret);
return ret;
}
srs_verbose("listen socket success. fd=%d", fd);
/*将监听套接字封装到协程中的套接字中*/
if ((stfd = st_netfd_open_socket(fd)) == NULL){
ret = ERROR_ST_OPEN_SOCKET;
srs_error("st_netfd_open_socket open socket failed. ret=%d", ret);
return ret;
}
srs_verbose("st open socket success. fd=%d", fd);
/*创建一个协程,用于监听这个套接字。*/
if ((tid = st_thread_create(listen_thread, this, 1, 0)) == NULL) {
ret = ERROR_ST_CREATE_LISTEN_THREAD;
srs_error("st_thread_create listen thread error. ret=%d", ret);
return ret;
}
srs_verbose("create st listen thread success.");
srs_trace("server started, listen at port=%d, fd=%d", port, fd);
return ret;
}
创建listen socket fd,并将其封装成st中的_st_netfd_t,最后创建一个协程用于监听客户端的连接。
void* SrsListener::listen_thread(void* arg)
{
SrsListener* obj = (SrsListener*)arg;
srs_assert(obj != NULL);
obj->loop = true;
obj->listen_cycle(); /*执行监听事件循环*/
return NULL;
}
协程的入口函数,注意此函数必须是类的静态成员函数。
void SrsListener::listen_cycle()
{
int ret = ERROR_SUCCESS;
log_context->generate_id();
srs_trace("listen cycle start, port=%d, type=%d, fd=%d", port, type, fd);
while (loop) {
/*调用st中的st_accept函数,等待客户端的连接。*/
st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
if(client_stfd == NULL){
srs_warn("ignore accept thread stoppped for accept client error");
continue;
}
srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd));
/*通知server,有新的客户端连接进来。*/
if ((ret = server->accept_client(type, client_stfd)) != ERROR_SUCCESS) {
srs_warn("accept client error. ret=%d", ret);
continue;
}
srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret);
}
}
listen协程会"阻塞"在st_accept函数上,当有客户端连接进来时,listen协程会从st_accept返回。
运行主协程
int SrsServer::cycle()
{
int ret = ERROR_SUCCESS;
while (true) {
st_usleep(SRS_TIME_RESOLUTION_MS * 1000); /*睡眠500ms*/
srs_update_system_time_ms(); /*更新当前时间*/
if (signal_reload) { /*是否重载载入配置文件*/
signal_reload = false;
srs_info("get signal reload, to reload the config.");
if ((ret = config->reload()) != ERROR_SUCCESS) { /*重新载入配置文件*/
srs_error("reload config failed. ret=%d", ret);
return ret;
}
srs_trace("reload config success.");
}
}
return ret;
}
在_server()->listen()中创建listen协程后,此协程还没有得到CPU的执行权,会main函数中接着执行。执行到SrsServer::cycle函数后,主协程
会进入while循环。当主协程
执行st_usleep函数时,会让出CPU的执行权,st会重新调度就绪队列中的协程。此时就绪队列中只有上面刚刚创建的listen协程,listen协程获取CPU执行权后,会一直执行到st_accept函数,因为此时没有客户端连接,所以listen协程会让出CPU执行权,再次进入st的调度程序中。此时就绪队列中没有任何就绪的协程,所以CPU的执行权进入了idle协程,idle协程会通过超时阻塞在epoll_wait上等待客户端的连接,使用了带有超时的epoll_wait,所以idle协程可以处理主协程
的睡眠事件。
主协程
在睡眠的同时会监视signal_reload变量,当通过SIGHUB信号将其设置为true时,主协程
会进入配置文件重载处理程序中。
总结
srs启动后会有三个重要的协程被创建,分别是:idle协程、listen协程、主协程
。
idle协程:在没有就绪协程时执行,用于监听读、写、定时器事件。
listen协程:用于监听客户端的连接。
主协程
:用于更新时间和配置文件的重载。