srs源码分析3-srs的启动

本文分析的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协程:用于监听客户端的连接。

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值