本文分析的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 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协程:用于监听客户端的连接。
主协程:用于更新时间和配置文件的重载。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/212271.html原文链接:https://javaforall.net
