本文分析的srs版本是0.6.0

srs源码分析1-搭建环境

srs源码分析2-浅析state_threads

srs源码分析3-srs的启动

srs源码分析4-客户端的连接

srs源码分析5-handshake

srs源码分析6-connect

以下正在写作中。。。

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 SIGHUPsignal(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协程:用于监听客户端的连接。

主协程:用于更新时间和配置文件的重载。

srs源码分析3-srs的启动相关推荐

  1. janusgraph源码分析1-下载编译启动 1

    date: 2018-04-26 title: "janusgraph源码分析1-下载编译启动" author: "邓子明" tags: - 源码 - janu ...

  2. SRS源码分析-rtmp转rtc流程

    前言 SRS4.0支持将RTMP流转换成RTC流,本文将结合源码分析下这个过程. 配置 首先,需要在SRS4.0的启动配置文件里面开启RTC Server和RTC 能力,可以参考官方提供的配置文件./ ...

  3. Solr初始化源码分析-Solr初始化与启动

    用solr做项目已经有一年有余,但都是使用层面,只是利用solr现有机制,修改参数,然后监控调优,从没有对solr进行源码级别的研究.但是,最近手头的一个项目,让我感觉必须把solrn内部原理和扩展机 ...

  4. Android Instrumentation源码分析(附Activity启动流程)

    转载请注明出处:http://blog.csdn.net/ahence/article/details/54959235 Instrumentation概念 官方说明 Instrumentation类 ...

  5. uboot源码分析(基于S5PV210)之启动第一阶段

    目录 一.start.S引入 1.u-boot.lds中找到start.S入口 2.SourceInsight中如何找到文件 3.SI中找文件技巧 二.start.S解析 1.不简单的头文件包含 2. ...

  6. Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service

    上一章:Android 双开沙箱 VirtualApp 源码分析(三)App 启动 原生 Service 创建过程 首先有必要了解一下原生 framework 对 Service 的创建,因为在 VA ...

  7. SpringMVC源码分析_1 SpringMVC容器启动和加载原理

                                                                    SpringMVC源码分析_1 SpringMVC启动和加载原理     ...

  8. k8s garbage collector源码分析(1)-启动分析

    k8s garbage collector分析(1)-启动分析 garbage collector介绍 Kubernetes garbage collector即垃圾收集器,存在于kube-contr ...

  9. Spring Cloud Eureka 源码分析(一) 服务端启动过程

    2019独角兽企业重金招聘Python工程师标准>>> 一. 前言 我们在使用Spring Cloud Eureka服务发现功能的时候,简单的引入maven依赖,且在项目入口类根据服 ...

最新文章

  1. [导入][转]好企业是什么样?
  2. TClientDataSet[28]: 读写其他格式的 XML 文件
  3. Ext.Net ASP.NET
  4. Java 中正确获取中文字符串长度
  5. 产品经理懂技术=流氓会武术(zz)
  6. java、sqlserver复习
  7. 铺磁砖,给定M*N的格子,用u*v的瓷砖去铺满,有多少种铺法
  8. 迅为4412开发板开发笔记--开发板与主机和ubuntu的ping通
  9. Elasticsearch实践(四):IK分词
  10. SI5341驱动(verilog)
  11. 常用的jar包下载网站(自用)
  12. gopup是疫情经济生活搜索指数数据接口
  13. 不用爬虫,也能写一个聚合搜索引擎
  14. 为什么越来越多的人放弃欧美市场,转做Starday日本市场?
  15. 关于SES2000找管线定位问题的实验
  16. android核心版本特性(官网)
  17. hadoop - hadoop2.6 伪分布式 示例 wordcount 分词 和 hdfs常用操作命令
  18. 【算力网络】算力网络的发展愿景及目标
  19. 《自拍教程44》Python adb一键截取Logcat日志
  20. 软电话 开源的和免费的大全

热门文章

  1. python : Tkinter布局
  2. 快递鸟智选物流API接口平台对接分享实例
  3. 2019年9月全国计算机公共基础题库【速刷版】
  4. Android整合网上资源以及个人对GreenDao数据库框架的理解与使用(android-studio开发)
  5. 51单片机实现万年历
  6. 基于全基因组的基因家族分析的初尝试
  7. 【实战场景】商城-折扣活动设计方案
  8. 【Java】9、Java IO 流
  9. 墨尘 - UE4 入门教程笔记 —— 二
  10. 【JZOJ6441】【GDOI2020模拟01.17】小 ω 维护序列