SRS流媒体服务器:RTMP端口监听逻辑分析

目录

  1. listen监听端口

  2. accept接收⼀个套接字中已建⽴的连接

  3. new SrsRtmpConn新建⼀个连接对象

这是SRS流媒体服务器教程第一篇,后续将会讲解:2、RTMP推流、拉流创建连接;3、服务器读取RTMP推流数据;4、服务器给RTMP拉流端转发数据。

1. RTMP端口监听逻辑

  1. RTMP基于TCP连接,需要进行:

  2. listen监听端口。

  3. accept接收⼀个套接字中已建⽴的连接。

  4. new⼀个RTMP连接对象关联accept得到的fd。

1. listen监听端口

  1. srs配置文件为:

listen              1935;
max_connections     1000;
srs_log_tank        console;
daemon              off;
http_api {
    enabled         on;
    listen          1985;
}
http_server {
    enabled         on;
    listen          8080;
    dir             ./objs/nginx/html;
}
vhost __defaultVhost__ {
    hls {
        enabled         on;
    }
    http_remux {
        enabled     on;
        mount       [vhost]/[app]/[stream].flv;
    }
}

2.rtmp默认监听1935端口,字段是listen,所有的配置文件都是从srs_app_config.cpp获取。所有可以到srs_app_config.cpp中listen字段是在哪里使用的。对应代码如下:

vector<string> SrsConfig::get_listens()
{
    std::vector<string> ports;
    
    SrsConfDirective* conf = root->get("listen");
    if (!conf) {
        return ports;
    }
    
    for (int i = 0; i < (int)conf->args.size(); i++) {
        ports.push_back(conf->args.at(i));
    }
    
    return ports;
}

3.对SrsConfig::get_listens进行打断点,进入断点后查看调用栈为:

b srs_app_config.cpp:3045

Breakpoint 4, SrsConfig::get_listens[abi:cxx11]() (this=0xeb10b0) at src/app/srs_app_config.cpp:3045
3045	    std::vector<string> ports;
(gdb) bt
#0  SrsConfig::get_listens[abi:cxx11]() (this=0xeb10b0) at src/app/srs_app_config.cpp:3045
#1  0x00000000004eebce in SrsServer::listen_rtmp (this=0xef4d70) at src/app/srs_app_server.cpp:1169
#2  0x00000000004ebbad in SrsServer::listen (this=0xef4d70) at src/app/srs_app_server.cpp:752
#3  0x00000000005e4fc4 in SrsServerAdapter::run (this=0xef5770, wg=0x7fffffffe000) at src/app/srs_app_hybrid.cpp:160
#4  0x00000000005e5b26 in SrsHybridServer::run (this=0xedf740) at src/app/srs_app_hybrid.cpp:275
#5  0x000000000064f4ef in run_hybrid_server () at src/main/srs_main_server.cpp:476
#6  0x000000000064f010 in run_directly_or_daemon () at src/main/srs_main_server.cpp:406
#7  0x000000000064da17 in do_main (argc=3, argv=0x7fffffffe558) at src/main/srs_main_server.cpp:197
#8  0x000000000064dbc2 in main (argc=3, argv=0x7fffffffe558) at src/main/srs_main_server.cpp:206

 4.如上调用栈可以发现是在SrsServer::listen_rtmp中获取端口信息。SrsServer::listen_rtmp代码如下:

srs_error_t SrsServer::listen_rtmp()
{
    srs_error_t err = srs_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 SrsBufferListener(this, SrsListenerRtmpStream);
        listeners.push_back(listener);

        int port; string ip;
        srs_parse_endpoint(ip_ports[i], ip, port);
        
        if ((err = listener->listen(ip, port)) != srs_success) {
            srs_error_wrap(err, "rtmp listen %s:%d", ip.c_str(), port);
        }
    }
    
    return err;
}

5.srs会为每个端口创建一个SrsBufferListener对象,传入ip和端口调用其listen函数。

srs_error_t SrsBufferListener::listen(string i, int p)
{
    srs_error_t err = srs_success;
    
    ip = i;
    port = p;
    
    srs_freep(listener);
    listener = new SrsTcpListener(this, ip, port);
    
    if ((err = listener->listen()) != srs_success) {
        return srs_error_wrap(err, "buffered tcp listen");
    }
    
    string v = srs_listener_type2string(type);
    srs_trace("%s listen at tcp://%s:%d, fd=%d", v.c_str(), ip.c_str(), port, listener->fd());
    
    return err;
}

6.而SrsBufferListener::listen函数又会创建一个SrsTcpListener对象,调用其listen函数。

srs_error_t SrsTcpListener::listen()
{
    srs_error_t err = srs_success;

    if ((err = srs_tcp_listen(ip, port, &lfd)) != srs_success) {
        return srs_error_wrap(err, "listen at %s:%d", ip.c_str(), port);
    }
    
    srs_freep(trd);
    trd = new SrsSTCoroutine("tcp", this);
    if ((err = trd->start()) != srs_success) {
        return srs_error_wrap(err, "start coroutine");
    }
    
    return err;
}

7.每个监听SrsTcpListener对象对对象一个listen协程,listen协议负责accept。

8.trd->start() 函数最终会调用SrsTcpListener::cycle

srs_error_t SrsTcpListener::cycle()
{
    srs_error_t err = srs_success;
    
    while (true) {
        if ((err = trd->pull()) != srs_success) {
            return srs_error_wrap(err, "tcp listener");
        }
        
        srs_netfd_t fd = srs_accept(lfd, NULL, NULL, SRS_UTIME_NO_TIMEOUT); //等待新的fd
        if(fd == NULL){
            return srs_error_new(ERROR_SOCKET_ACCEPT, "accept at fd=%d", srs_netfd_fileno(lfd));
        }
        
        if ((err = srs_fd_closeexec(srs_netfd_fileno(fd))) != srs_success) {
            return srs_error_wrap(err, "set closeexec");
        }
        
        if ((err = handler->on_tcp_client(fd)) != srs_success) { //handler是SrsBufferListener,回调对应的业务处理函数
            return srs_error_wrap(err, "handle fd=%d", srs_netfd_fileno(fd));
        }
    }
    
    return err;
}

9.当有RTMP客户端请求连接时触发该断点,比如进行RTMP推流,则会进入handler->on_tcp_client。

10.handler->on_tcp_client函数,其中handler是SrsBufferListener,会回调SrsBufferListener::on_tcp_client函数。

11.最终RTMP业务回调到SrsServer::accept_client函数。

本文结尾底部,领取最新最全C++音视频学习提升资料,内容包括(C/C++Linux 服务器开发,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓文章底部

 

2. accept接收口个套接字中已建⽴的连接

  1. SrsServer::accept_client函数如下:

  2. srs_error_t SrsBufferListener::on_tcp_client(srs_netfd_t stfd)
    {
        srs_error_t err = server->accept_client(type, stfd);
        if (err != srs_success) {
            srs_warn("accept client failed, err is %s", srs_error_desc(err).c_str());
            srs_freep(err);
        }
        
        return srs_success;
    }
    

    2.进入server->accept_client函数。

srs_error_t SrsServer::accept_client(SrsListenerType type, srs_netfd_t stfd)
{
    srs_error_t err = srs_success;
    
    ISrsStartableConneciton* conn = NULL;
    
    if ((err = fd_to_resource(type, stfd, &conn)) != srs_success) {
        if (srs_error_code(err) == ERROR_SOCKET_GET_PEER_IP && _srs_config->empty_ip_ok()) {
            srs_close_stfd(stfd); srs_error_reset(err);
            return srs_success;
        }
        return srs_error_wrap(err, "fd to resource");
    }
    srs_assert(conn);
    
    // directly enqueue, the cycle thread will remove the client.
    conn_manager->add(conn);

    if ((err = conn->start()) != srs_success) { //每个conn对应一个协程
        return srs_error_wrap(err, "start conn coroutine");
    }
    
    return err;
}

3. new SrsRtmpConn新建⼀个连接对象

其中SrsServer::fd_to_resource根据创建SrsBufferListener对象时传入的SrsListenerRtmpStream参数创建SrsRtmpConn对象。

不管RTMP是推流还是拉流,每个连接在服务器都对应⼀个SrsRtmpConn对象,SrsRtmpConn持有连接对应的fd。

srs_error_t SrsServer::fd_to_resource(SrsListenerType type, srs_netfd_t stfd, ISrsStartableConneciton** pr)
{
    srs_error_t err = srs_success;
    
    int fd = srs_netfd_fileno(stfd);
    string ip = srs_get_peer_ip(fd);
    int port = srs_get_peer_port(fd);
    
    ...

    // The context id may change during creating the bellow objects.
    SrsContextRestore(_srs_context->get_id());
    
    if (type == SrsListenerRtmpStream) {
        *pr = new SrsRtmpConn(this, stfd, ip, port);
    } else if (type == SrsListenerHttpApi) {
        *pr = new SrsHttpApi(false, this, stfd, http_api_mux, ip, port);
    } else if (type == SrsListenerHttpsApi) {
        *pr = new SrsHttpApi(true, this, stfd, http_api_mux, ip, port);
    } else if (type == SrsListenerHttpStream) {
        *pr = new SrsResponseOnlyHttpConn(false, this, stfd, http_server, ip, port);
    } else if (type == SrsListenerHttpsStream) {
        *pr = new SrsResponseOnlyHttpConn(true, this, stfd, http_server, ip, port);
    } else {
        srs_warn("close for no service handler. fd=%d, ip=%s:%d", fd, ip.c_str(), port);
        srs_close_stfd(stfd);
        return err;
    }
    
    return err;
}

 3.创建SrsRtmpConn。

SrsRtmpConn::SrsRtmpConn(SrsServer* svr, srs_netfd_t c, string cip, int cport)
{
    // Create a identify for this client.
    _srs_context->set_id(_srs_context->generate_id());

    server = svr;

    stfd = c;
    skt = new SrsTcpConnection(c);
    manager = svr;
    ip = cip;
    port = cport;
    create_time = srsu2ms(srs_get_system_time());
    clk = new SrsWallClock();
    kbps = new SrsKbps(clk);
    kbps->set_io(skt, skt);
    trd = new SrsSTCoroutine("rtmp", this, _srs_context->get_id());
    
    rtmp = new SrsRtmpServer(skt);
    refer = new SrsRefer();
    bandwidth = new SrsBandwidth();
    security = new SrsSecurity();
    duration = 0;
    wakable = NULL;
    
    mw_sleep = SRS_PERF_MW_SLEEP;
    mw_msgs = 0;
    realtime = SRS_PERF_MIN_LATENCY_ENABLED;
    send_min_interval = 0;
    tcp_nodelay = false;
    info = new SrsClientInfo();

    publish_1stpkt_timeout = 0;
    publish_normal_timeout = 0;
    
    _srs_config->subscribe(this);
}```

4. 最终conn对象会调用start()启动一个协程,每个conn对应一个协程。

```cpp
srs_error_t SrsRtmpConn::start()
{
    srs_error_t err = srs_success;

    if ((err = skt->initialize()) != srs_success) {
        return srs_error_wrap(err, "init socket");
    }

    if ((err = trd->start()) != srs_success) {
        return srs_error_wrap(err, "coroutine");
    }

    return err;
}

4.对应调用栈:

(gdb) bt
#0  SrsRtmpConn::start (this=0x103fcf0) at src/app/srs_app_rtmp_conn.cpp:1442
#1  0x00000000004f0261 in SrsServer::accept_client (this=0xef4d70, type=SrsListenerRtmpStream, stfd=0x103fcc0) at src/app/srs_app_server.cpp:1378
#2  0x00000000004e7ca6 in SrsBufferListener::on_tcp_client (this=0xf43ef0, stfd=0x103fcc0) at src/app/srs_app_server.cpp:110
#3  0x00000000005c2211 in SrsTcpListener::cycle (this=0xf440b0) at src/app/srs_app_listener.cpp:271
#4  0x00000000005379d8 in SrsFastCoroutine::cycle (this=0xf68200) at src/app/srs_app_st.cpp:272
#5  0x0000000000537a6e in SrsFastCoroutine::pfn (arg=0xf68200) at src/app/srs_app_st.cpp:287
#6  0x000000000065010b in _st_thread_main () at sched.c:363
#7  0x0000000000650988 in st_thread_create (start=0x7ffff6f07b78 <main_arena+88>

 5.SrsRtmpConn::start最终会调用到SrsRtmpConn::cycle处理连接业务。

srs_error_t SrsRtmpConn::cycle()
{
    srs_error_t err = do_cycle();

    // Notify manager to remove it.
    // Note that we create this object, so we use manager to remove it.
    manager->remove(this);

    // success.
    if (err == srs_success) {
        srs_trace("client finished.");
        return err;
    }

    // It maybe success with message.
    if (srs_error_code(err) == ERROR_SUCCESS) {
        srs_trace("client finished%s.", srs_error_summary(err).c_str());
        srs_freep(err);
        return err;
    }

    // client close peer.
    // TODO: FIXME: Only reset the error when client closed it.
    if (srs_is_client_gracefully_close(err)) {
        srs_warn("client disconnect peer. ret=%d", srs_error_code(err));
    } else if (srs_is_server_gracefully_close(err)) {
        srs_warn("server disconnect. ret=%d", srs_error_code(err));
    } else {
        srs_error("serve error %s", srs_error_desc(err).c_str());
    }

    srs_freep(err);
    return srs_success;
}

 原文链接

猜你喜欢

转载自blog.csdn.net/m0_60259116/article/details/125332099
今日推荐