1、线程模型
srs使用了state-threads协程库,是单线程多协程模型。 这个协程的概念类似于lua的协程,都是单线程中可以创建多个协程。而golang中的goroutine协程是多线程并发的,goroutine有可能运行在同一个线程也可能在不同线程,这样就有了线程安全问题,所以需要chan通信或者mutex加锁共享资源。 而srs因为是单线程多协程所以不用考虑线程安全,数据不用加锁。
2、主流程分析
撇掉程序启动的一些初始化和设置,直接进入:
int SrsServer::listen()
{
int ret = ERROR_SUCCESS;
if ((ret = listen_rtmp()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = listen_http_api()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = listen_http_stream()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = listen_stream_caster()) != ERROR_SUCCESS) {
return ret;
}
return ret;
}
先看看listen_rtmp()
:
int SrsServer::listen_rtmp()
{
int ret = ERROR_SUCCESS;
// stream service port.
std::vector<std::string> ip_ports = _srs_config->get_listens();
srs_assert((int)ip_ports.size() > 0);
close_listeners(SrsListenerRtmpStream);
for (int i = 0; i < (int)ip_ports.size(); i++) {
SrsListener* listener = new SrsStreamListener(this, SrsListenerRtmpStream);
listeners.push_back(listener);
std::string ip;
int port;
srs_parse_endpoint(ip_ports[i], ip, port);
if ((ret = listener->listen(ip, port)) != ERROR_SUCCESS) {
srs_error("RTMP stream listen at %s:%d failed. ret=%d", ip.c_str(), port, ret);
return ret;
}
}
return ret;
}
创建了SrsStreamListener
,在SrsStreamListener::listen
中又创建了SrsTcpListener
进行listen
。
SrsTcpListener::SrsTcpListener(ISrsTcpHandler* h, string i, int p)
{
handler = h;
ip = i;
port = p;
_fd = -1;
_stfd = NULL;
pthread = new SrsReusableThread("tcp", this);
}
在SrsTcpListener
中创建了pthread: SrsReusableThread
。 在int SrsTcpListener::listen()
中调用了pthread->start()
,协程会回调到int SrsTcpListener::cycle()
。
int SrsTcpListener::cycle()
{
int ret = ERROR_SUCCESS;
st_netfd_t client_stfd = st_accept(_stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
if(client_stfd == NULL){
// ignore error.
if (errno != EINTR) {
srs_error("ignore accept thread stoppped for accept client error");
}
return ret;
}
srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd));
if ((ret = handler->on_tcp_client(client_stfd)) != ERROR_SUCCESS) {
srs_warn("accept client error. ret=%d", ret);
return ret;
}
return ret;
}
accept
连接后,回调到on_tcp_client
。 也就是SrsStreamListener::on_tcp_client
:
int SrsStreamListener::on_tcp_client(st_netfd_t stfd)
{
int ret = ERROR_SUCCESS;
if ((ret = server->accept_client(type, stfd)) != ERROR_SUCCESS) {
srs_warn("accept client error. ret=%d", ret);
return ret;
}
return ret;
}
int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd)
{
...
SrsConnection* conn = NULL;
if (type == SrsListenerRtmpStream) {
conn = new SrsRtmpConn(this, client_stfd);
} else if (type == SrsListenerHttpApi) {
#ifdef SRS_AUTO_HTTP_API
conn = new SrsHttpApi(this, client_stfd, http_api_mux);
#else
srs_warn("close http client for server not support http-api");
srs_close_stfd(client_stfd);
return ret;
#endif
} else if (type == SrsListenerHttpStream) {
#ifdef SRS_AUTO_HTTP_SERVER
conn = new SrsResponseOnlyHttpConn(this, client_stfd, http_server);
#else
srs_warn("close http client for server not support http-server");
srs_close_stfd(client_stfd);
return ret;
#endif
} else {
// TODO: FIXME: handler others
}
srs_assert(conn);
// directly enqueue, the cycle thread will remove the client.
conns.push_back(conn);
srs_verbose("add conn to vector.");
// cycle will start process thread and when finished remove the client.
// @remark never use the conn, for it maybe destroyed.
if ((ret = conn->start()) != ERROR_SUCCESS) {
return ret;
}
srs_verbose("conn started success.");
srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret);
return ret;
}
在上面根据type创建不同的SrsConnection
,Rtmp
创建了SrsRtmpConn
,并且加入到std::vector<SrsConnection*> conns;
中,然后执行conn->start()
。
SrsConnection
基类创建了一个协程pthread: SrsOneCycleThread
,上面的conn->start()
,实际上是pthread->start()
:
SrsConnection::SrsConnection(IConnectionManager* cm, st_netfd_t c)
{
id = 0;
manager = cm;
stfd = c;
disposed = false;
expired = false;
// the client thread should reap itself,
// so we never use joinable.
// TODO: FIXME: maybe other thread need to stop it.
// @see: https://github.com/ossrs/srs/issues/78
pthread = new SrsOneCycleThread("conn", this);
}
int SrsConnection::start()
{
return pthread->start();
}
在int SrsConnection::cycle()
调用了do_cycle()
,派生类实现了这个方法。
int SrsRtmpConn::do_cycle()
{
int ret = ERROR_SUCCESS;
srs_trace("RTMP client ip=%s", ip.c_str());
rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US);
rtmp->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US);
//正式进入rtmp握手。
if ((ret = rtmp->handshake()) != ERROR_SUCCESS) {
srs_error("rtmp handshake failed. ret=%d", ret);
return ret;
}
srs_verbose("rtmp handshake success");
if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) {
srs_error("rtmp connect vhost/app failed. ret=%d", ret);
return ret;
}
srs_verbose("rtmp connect app success");
// set client ip to request.
req->ip = ip;
srs_trace("connect app, "
"tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, args=%s",
req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(),
req->schema.c_str(), req->vhost.c_str(), req->port.c_st