上次谈到端口侦听后,如果有连接上来,会一直回调到server的accept_client函数里。下面来一步一步分析这个函数。并分析下srs的连接框架
int max_connections = _srs_config->get_max_connections();
if ((int)conns.size() >= max_connections) {
srs_error("exceed the max connections, drop client: "
"clients=%d, max=%d, fd=%d", (int)conns.size(), max_connections, fd);
srs_close_stfd(client_stfd);
return ret;
}
首先检测是否达到了最大连接数。这个连接数是所有连接,包括rtmp http rtsp等的连接数。这个最大连接数可以在配置文件中配置。默认为SRS_CONF_DEFAULT_MAX_CONNECTIONS =1000
if (true) {
int val;
if ((val = fcntl(fd, F_GETFD, 0)) < 0) {
ret = ERROR_SYSTEM_PID_GET_FILE_INFO;
srs_error("fnctl F_GETFD error! fd=%d. ret=%#x", fd, ret);
srs_close_stfd(client_stfd);
return ret;
}
val |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, val) < 0) {
ret = ERROR_SYSTEM_PID_SET_FILE_INFO;
srs_error("fcntl F_SETFD error! fd=%d ret=%#x", fd, ret);
srs_close_stfd(client_stfd);
return ret;
}
}
这段代码的作用见 https://github.com/ossrs/srs/issues/518。说是为了避免fd泄露。我没有看明白。
接下来要创建具体的连接实例
if (type == SrsListenerRtmpStream) {
conn = new SrsRtmpConn(this, client_stfd);
}
并把这个实例放入本地的数据结构中
conns.push_back(conn);
最后调用start()函数。就结束了
if ((ret = conn->start()) != ERROR_SUCCESS) {
return ret;
}
比较简单是不。st编程我还没有琢磨透,不过这个的确超出了我的预期。接着我们来分析连接框架。了解下主函数调用了start()之前和之后都发生了什么事情。先来看下连接的继承关系
由于st线程的便利性。所以srs对待连接和对待侦听的思路是一样的,就是直接开一个线程自生自灭。我们看到连接的基类SrsConnection里,有一个线程变量
SrsOneCycleThread* pthread;
很有意思的一个线程类,这个应该叫做循环一次就死的线程类,查看其循环函数
int SrsOneCycleThread::cycle()
{
int ret = handler->cycle();
pthread->stop_loop();
return ret;
}
果然是在处理类cycle()一次后,就关闭线程。担任如果handler一直不退出cycle(),这个也可以演变成endlesss的类。
为啥连接类要用这个线程基类呢?我想可能是希望处理完连接请求就自动关闭掉吧。特别是大量的http请求。关闭的时候,第二个变量 IConnectionManager* manager就其作用了。看看 IConnectionManager的接口
virtual void remove(SrsConnection* c) = 0;
其实现在server类里
void SrsServer::remove(SrsConnection* conn)
{
std::vector<SrsConnection*>::iterator it = std::find(conns.begin(), conns.end(), conn);
// removed by destroy, ignore.
if (it == conns.end()) {
srs_warn("server moved connection, ignore.");
return;
}
conns.erase(it);
srs_info("conn removed. conns=%d", (int)conns.size());
SrsStatistic* stat = SrsStatistic::instance();
stat->kbps_add_delta(conn);
stat->on_disconnect(conn->srs_id());
// all connections are created by server,
// so we free it here.
srs_freep(conn);
}
这样变实现了连接的自动释放。设计还是比较巧妙滴。
下面来分析下rtmp的连接实例。由于在基类里,
virtual int do_cycle() = 0;
int SrsConnection::cycle()
{
int ret = ERROR_SUCCESS;
_srs_context->generate_id();
id = _srs_context->get_id();
ip = srs_get_peer_ip(st_netfd_fileno(stfd));
ret = do_cycle();
// if socket io error, set to closed.
if (srs_is_client_gracefully_close(ret)) {
ret = ERROR_SOCKET_CLOSED;
}
// success.
if (ret == ERROR_SUCCESS) {
srs_trace("client finished.");
}
// client close peer.
if (ret == ERROR_SOCKET_CLOSED) {
srs_warn("client disconnect peer. ret=%d", ret);
}
return ERROR_SUCCESS;
}
所有的派生连接类,基本就围绕这个纯虚函数来搞动作。
下面要开始大戏分析了。rtmp协议的实现。在这里需要分析如下几个点
1)如何实现rtmp servr连接
2)怎么实现hook
3) 怎么实现edge
4)怎么实现forward
6)怎么实现统计
在这之前,我需要在仔细回顾下rmtp协议文档。