目录
-
listen监听端口
-
accept接收⼀个套接字中已建⽴的连接
-
new SrsRtmpConn新建⼀个连接对象
这是SRS流媒体服务器教程第一篇,后续将会讲解:2、RTMP推流、拉流创建连接;3、服务器读取RTMP推流数据;4、服务器给RTMP拉流端转发数据。
1. RTMP端口监听逻辑
-
RTMP基于TCP连接,需要进行:
-
listen监听端口。
-
accept接收⼀个套接字中已建⽴的连接。
-
new⼀个RTMP连接对象关联accept得到的fd。
1. listen监听端口
-
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接收口个套接字中已建⽴的连接
-
SrsServer::accept_client函数如下:
-
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;
}