SRS之RTMP的TCP线程(即监听线程)
本文分析的是 SRS 针对 rtmp 的端口建立的 tcp 线程。具体建立过程: SRS之监听端口的管理:RTMP
RTMP 的 TCP 线程中各个类之间 handler 的关系图
1. RTMP之TCP线程:SrsThread::thread_fun
void* SrsThread::thread_fun(void* arg)
{
SrsThread* obj = (SrsThread*)arg;
srs_assert(obj);
/* 进入线程主循环 */
obj->thread_cycle();
// for valgrind to detect.
SrsThreadContext* ctx = dynamic_cast<SrsThreadContext*>(_srs_context);
if (ctx) {
ctx->clear_cid();
}
st_thread_exit(NULL);
return NULL;
}
1.1 SrsThread::thread_cycle()
void SrsThread::thread_cycle()
{
int ret = ERROR_SUCCESS;
_srs_context->generate_id();
srs_info("thread %s cycle start", _name);
_cid = _srs_context->get_id();
srs_assert(handler);
/* 父类 ISrsThreadHandler 指针 handler 调用子类对象
* SrsReusableThread 的成员函数 on_thread_start */
handler->on_thread_start();
// thread is running now.
really_terminated = false;
// wait for cid to ready, for parent thread to get the cid.
while (!can_run && loop) {
st_usleep(10 * 1000);
}
while (loop) {
/* 调用子类SrsReusableThread的成员函数on_before_cycle */
if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {
srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d",
_name, ret);
goto failed;
}
srs_info("thread %s on before cycle success", _name);
/* 调用子类 SrsReusableThread 的成员函数 cycle */
if ((ret = handler->cycle()) != ERROR_SUCCESS) {
if (!srs_is_client_gracefully_close(ret) && !srs_is_system_control_error(ret))
{
srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret);
}
goto failed;
}
srs_info("thread %s cycle success", _name);
/* 调用子类 SrsReusableThread 的成员函数 on_end_cycle */
if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d",
_name, ret);
goto failed;
}
srs_info("thread %s on end cycle success", _name);
failed:
/* 检测是否仍然继续循环 */
if (!loop) {
break;
}
/* 每次循环完成一次后,就休眠 cycle_interval_us(>0) 微妙 */
// to improve performance, donot sleep when interval is zero.
// @see: https://github.com/ossrs/srs/issues/237
if (cycle_interval_us != 0) {
st_usleep(cycle_interval_us);
}
}
// readly terminated now.
really_terminated = true;
handler->on_thread_stop();
srs_info("thread %s cycle finished", _name);
}
1.2 SrsReusableThread::on_thread_start
void SrsReusableThread::on_thread_start()
{
/* 由于子类 SrsTcpListener 没有实现 on_thread_start,
* 因此调用的是父类 ISrsReusableThreadHandler 的 on_thread_start,
* 但是,该父类实现的函数为空 */
handler->on_thread_start();
}
void ISrsReusableThreadHandler::on_thread_start()
{
}
1.3 SrsReusableThread::on_before_cycle
int SrsReusableThread::on_before_cycle()
{
/* 由于子类 SrsTcpListener 没有实现 on_before_cycle,
* 因此调用的是父类 ISrsReusableThreadHandler 的 on_before_cycle */
return handler->on_before_cycle();
}
int ISrsReusableThreadHandler::on_before_cycle()
{
return ERROR_SUCCESS;
}
1.4 SrsReusableThread::cycle
int SrsReusableThread::cycle()
{
/* 调用子类 SrsTcpListener 实现的 cycle 函数 */
return handler->cycle();
}
1.5 SrsTcpListener::cycle
位于 srs_app_listener.cpp:
int SrsTcpListener::cycle()
{
int ret = ERROR_SUCCESS;
/* 调用ST的库函数st_accept监听接收客户端的连接请求 */
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));
/* 调用子类SrsStreamListener的on_tcp_client函数 */
if ((ret = handler->on_tcp_client(client_stfd)) != ERROR_SUCCESS) {
srs_warn("accept client error. ret=%d", ret);
return ret;
}
return ret;
}
1.5.1 st_accept
#ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT
_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen,
st_utime_t timeout)
{
int osfd, err;
_st_netfd_t *newfd;
/* 调用accept接受客户端的连接请求,若当前没有客户端请求连接,即accept返回失败,
* 则会调用st_netfd_poll函数向epoll添加fd的POLLIN事件,即监听该fd是否可读,
* 然后当前线程让出控制权,调度其他线程运行,直到有客户端连接请求到来时,
* 才会再次调度该线程,accept 该连接请求 */
while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) {
if (errno == EINTR)
continue;
if (!_IO_NOT_READY_ERROR)
return NULL;
/* Wait until the socket becomes readable */
if (st_netfd_poll(fd, POLLIN, timeout) < 0)
return NULL;
}
/* On some platforms the new socket created by accept() inherits */
/* the nonblocking attribute of the listening socket */
#if defined(MD_ACCEPT_NB_INHERITED)
newfd = _st_netfd_new(osfd, 0, 1);
#elif defined(MD_ACCEPT_NB_NOT_INHERITED)
newfd = _st_netfd_new(osfd, 1, 1);
#else
#error Unknown OS
#endif
if (!newfd) {
err = errno;
close(osfd);
errno = err;
}
return newfd;
}
#else
#endif
1.6 SrsStreamListener::on_tcp_client
/**
* @stfd: 保存的是accept客户端连接后生成的已连接套接字信息
*/
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;
}
1.7 SrsServer::accept_client
位于 srs_app_server.cpp 中:
int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd)
{
int ret = ERROR_SUCCESS;
int fd = st_netfd_fileno(client_stfd);
/* 获取配置文件中限定的最大连接数 */
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;
}
// avoid fd leak when fork.
// @see https://github.com/ossrs/srs/issues/518
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;
}
/* 给该已连接套接字 fd 设置执行时关闭标志 */
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;
}
}
SrsConnection *conn = NULL;
if (type == SrsListenerRtmpStream) {
/* 当为 RTMP 连接时,构造一个 RTMP 连接 */
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-server");
srs_close_stfd(client_stfd);
return ret;
#endif
} else {
// TODO: FIXME: handler others
}
srs_assert(conn);
/* 将新构造的 conn 放到 SrsServer 类中的成员 conns 容器中,该容器保存着当前
* SRS 所有的连接 */
// 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.
/* 调用子类 SrsRtmpConn 实现有 start 虚函数,则调用子类的start,这里子类没有实现,
* 因此调用的是父类的 start 函数,该函数最终会调用 st_thread_create 函数创建一个
* 线程 */
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;
}
1.8 构造 SrsRtmpConn
1.8.1 SrsRtmpConn 类定义
/**
* the client provides the main logic control for RTMP clients.
*/
class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandler
{
// for the thread to directly access any field of connection.
friend class SrsPublishRecvThread;
private:
SrsServer* server;
SrsRequest* req;
SrsResponse* res;
SrsStSocket* skt;
SrsRtmpServer* rtmp;
SrsRefer* refer;
SrsBandwidth* bandwidth;
SrsSecurity* security;
// the wakable handler, maybe NULL.
ISrsWakable* wakable;
// elapse duration in ms
// for live play duration, for instance, rtmpdump to record.
// @see https://github.com/ossrs/srs/issues/47
int64_t duration;
SrsKbps* kbps;
// the MR(merged-write) sleep time in ms.
int mw_sleep;
// the MR(merged-write) only enabled for play.
int mw_enabled;
// for realtime;
// @see https://github.com/ossrs/srs/issues/257
bool realtime;
// the minimal interval in ms for delivery stream.
double send_min_interval;
// publish 1st packet timeout in ms
int publish_1stpkt_timeout;
// publish normal packet timeout in ms
int publish_normal_timeout;
// whether enable the tcp_nodelay.
bool tcp_nodelay;
// The type of client, play or publish.
SrsRtmpConnType client_type;
public:
SrsRtmpConn(SrsServer* svr, st_netfd_t c);
virtual ~SrsRtmpConn();
public:
virtual void dispose();
protected:
virtual int do_cycle();
// interface ISrsReloadHandler
public:
virtual int on_reload_vhost_removed(std::string vhost);
virtual int on_reload_vhost_mw(std::string vhost);
virtual int on_reload_vhost_smi(std::string vhost);
virtual int on_reload_vhost_tcp_nodelay(std::string vhost);
virtual int on_reload_vhost_realtime(std::string vhost);
virtual int on_reload_vhost_p1stpt(std::string vhost);
virtual int on_reload_vhost_pnt(std::string vhost);
// interface IKbpsDelta
public:
virtual void resample();
virtual int64_t get_send_bytes_delta();
virtual int64_t get_recv_bytes_delta();
virtual void cleanup();
private:
// when valid and connected to vhost/app, service the client.
virtual int service_cycle();
// stream(play/publish) service cycle, identify client first.
virtual int stream_service_cycle();
virtual int check_vhost();
virtual int playing(SrsSource* source);
virtual int do_playing(SrsSource* source, SrsConsumer* consumer,
SrsQueueRecvThread* trd);
virtual int publishing(SrsSource* source);
virtual int do_publishing(SrsSource* source, SrsPublishRecvThread* trd);
virtual int acquire_publish(SrsSource* source, bool is_edge);
virtual void release_publish(SrsSource* source, bool is_edge);
virtual int handle_publish_message(SrsSource* source, SrsCommonMessage* msg,
bool is_fmle, bool vhost_is_edge);
virtual int process_publish_message(SrsSource* source, SrsCommonMessage* msg,
bool vhost_is_edge);
virtual int process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg);
virtual void change_mw_sleep(int sleep_ms);
virtual void set_sock_options();
private:
virtual int check_edge_token_traverse_auth();
virtual int connect_server(int origin_index, st_netfd_t* pstsock);
virtual int do_token_traverse_auth(SrsRtmpClient* client);
private:
virtual int http_hooks_on_connect();
virtual void http_hooks_on_close();
virtual int http_hooks_on_publish();
virtual void http_hooks_on_unpublish();
virtual int http_hooks_on_play();
virtual void http_hooks_on_stop();
};
1.8.2 SrsConnection 类定义
/**
* the basic connection of SRS,
* all connections accept from listener must extends from this base class,
* server will add the connectin to manager, and delete it when remove.
*/
class SrsConnection : public virtual ISrsOneCycleThreadHandler, public virtual IKbpsDelta
{
private:
/**
* each connection start a green thread,
* when thread stop, the connection will be delete by server.
*/
SrsOneCycleThread* pthread;
/**
* the id of connection.
*/
int id;
protected:
/**
* the manager object to manager the connection.
*/
IConnectionManager* manager;
/**
* the underlayer st fd handler.
*/
st_netfd_t stfd;
/**
* the ip of client.
*/
std::string ip;
/**
* whether the connection is disposed,
* when disposed, connection should stop cycle and cleanup itself.
*/
bool disposed;
/**
* whether connection is expired, application definetion.
* when expired, the connection must never be served and quit ASAP.
*/
bool expired;
public:
SrsConnection(IConnectionManager* cm, st_netfd_t c);
virtual ~SrsConnection();
public:
/**
* to dipose the connection.
*/
virtual void dispose();
/**
* start the client green thread.
* when server get a client from listener,
* 1. server will create an concrete connection(for instance, RTMP connection),
* 2. then add connection to its connection manager
* 3. start the client thread by invoke this start()
* when client cycle thread stop, invoke the on_thread_stop(), which will use server
* to remove the client by server->remove(this);
*/
virtual int start();
// interface ISrsOneCycleThreadHandler
public:
/**
* the thread cycle function,
* when serve connection completed, terminater the loop which will terminater the
* thread, thread will invoke the on_thread_stop() when it terminated.
*/
virtual int cycle();
/**
* when then thread cycle finished, thread will invoke the on_thread_stop(),
* which will remove self from server, server will remove the connection from manager
* then delete the connection.
*/
virtual void on_thread_stop();
public:
/**
* get the srs id which identify the client.
*/
virtual int srs_id();
/**
* set connection to expired.
*/
virtual void expire();
protected:
/**
* for concrete connection to do the cycle.
*/
virtual int do_cycle() = 0;
};
1.8.3 ISrsOneCycleThreadHandler 类定义
/**
* the one cycle thread is a thread do the cycle only one time,
* that is, the thread will quit when return from the cycle.
* user can create thread which stop itself,
* generally only need to provides a start method,
* the object will destroy itself then terminate the thread, @see SrsConnection
* 1. create SrsThread field
* 2. the thread quit when return from cycle.
* for example:
* class SrsConnection : public ISrsOneCycleThreadHandler {
* public: SrsConnection() { pthread = new SrsOneCycleThread("conn", this); }
* public: virtual int start() { return pthread->start(); }
* public: virtual int cycle() {
* // serve client.
* // set loop to stop to quit, stop thread itself.
* pthread->stop_loog();
* }
* public: virtual void on_thread_stop() {
* // remove the connection in thread itself.
* server->remove(this);
* }
}
* };
*/
class ISrsOneCycleThreadHandler
{
public:
ISrsOneCycleThreadHandler();
virtual ~ISrsOneCycleThreadHandler();
public:
/**
* the cycle method for the one cycle thread.
*/
virtual int cycle() = 0;
public:
/**
* other callback for handler.
* @remark all callback is optional, handler can ignore it.
*/
virtual void on_thread_start();
virtual int on_before_cycle();
virtual int on_end_cycle();
virtual void on_thread_stop();
};
1.8.4 IKbpsDelta 类定义
/**
* the interface which provices delta of bytes.
* for a delta, for example, a live stream connection, we can got the delta by:
* IKbpsDelta* delta = ...;
* delta->resample();
* kbps->add_delta(delta);
* delta->cleanup();
*/
class IKbpsDelta
{
public:
IKbpsDelta();
virtual ~IKbpsDelta();
public:
/**
* resample to generate the value of delta bytes.
*/
virtual void resample() = 0;
/**
* get the send or recv bytes delta.
*/
virtual int64_t get_send_bytes_delta() = 0;
virtual int64_t get_recv_bytes_delta() = 0;
/**
* cleanup the value of delta bytes.
*/
virtual void cleanup() = 0;
};
上面几个类之间的关系图
构造 SrsRtmpConn 类,首先调用的是其父类 SrsConnection 的构造函数.
1.8.3 SrsConnection 构造函数
位于 srs_app_conn.cpp:
SrsConnection::SrsConnection(IConnectionManager* cm, st_netfd_t c)
{
id = 0;
/* manager 是 IConnectionManager 类的指针,该指针管理着当前连接.
* cm 是 由构造 SrsRtmpConn 传入的表示当前 SrsServer 类的 this 指针,
* 而 IConnectionManager 类是 SrsServer 的父类,因此,
* 这里是父类 IConnectionManager 指针 manager 指向子类 SrsServer 指针 this */
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 是 SrsOneCycleThread 类的指针,该指针指向新构造的
* SrsOneCycleThread 类结构体对象 */
pthread = new SrsOneCycleThread("conn", this);
}
在该函数中接着构造 SrsOneCycleThread 类。
1.8.4 构造 SrsOneCycleThread
1.8.4.1 SrsOneCycleThread 类定义
class SrsOneCycleThread : public internal::ISrsThreadHandler
{
private:
internal::SrsThread* pthread;
ISrsOneCycleThreadHandler* handler;
public:
SrsOneCycleThread(const char* n, ISrsOneCycleThreadHandler* h);
virtual ~SrsOneCycleThread();
public:
/**
* for the cycle thread, quit when cycle return.
*/
virtual int start();
// interface internal::ISrsThreadHandler
public:
virtual int cycle();
virtual void on_thread_start();
virtual int on_before_cycle();
virtual int on_end_cycle();
virtual void on_thread_stop();
};
1.8.4.2 SrsOneCycleThread 构造函数
SrsOneCycleThread::SrsOneCycleThread(const char* n, ISrsOneCycleThreadHandler* h)
{
/* handler 是 ISrsOneCycleThreadHandler 类的指针,h 是传入的 SrsConnection 类的
* this 指针,ISrsOneCycleThreadHandler 类是 SrsConnection 类的父类 */
handler = h;
/* pthread 是指向 internal::SrsThread 类的指针,pthread 指向新构造的
* internal::SrsThread 类对象. */
pthread = new internal::SrsThread(n, this, 0, false);
}
1.8.4.3 internal::SrsThread 构造函数
位于 srs_app_thread.cpp:
SrsThread::SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable)
{
/* 该线程的名称,由前面可知为 "conn" */
_name = name;
/* handler 是 ISrsThreadHandler 类的指针,thread_handler 是传入的 SrsOneCycleThread 类的
* this 指针,ISrsThreadHandler 是 SrsOneCycleThread 的父类 */
handler = thread_handler;
cycle_interval_us = interval_us;
tid = NULL;
loop = false;
really_terminated = true;
_cid = -1;
_joinable = joinable;
disposed = false;
// in start(), the thread cycle method maybe stop and remove the thread itself,
// and the thread start() is waiting for the _cid, and segment fault then.
// @see https://github.com/ossrs/srs/issues/110
// thread will set _cid, callback on_thread_start(), then wait for the can_run signal.
can_run = false;
}
1.8.5 SrsRtmpConn 构造函数
位于 srs_app_rtmp_conn.cpp 中:
SrsRtmpConn::SrsRtmpConn(SrsServer* svr, st_netfd_t c)
: SrsConnection(svr, c)
{
server = svr;
req = new SrsRequest();
res = new SrsResponse();
skt = new SrsStSocket(c);
rtmp = new SrsRtmpServer(skt);
refer = new SrsRefer();
bandwidth = new SrsBandwidth();
security = new SrsSecurity();
duration = 0;
kbps = new SrsKbps();
kbps->set_io(skt, skt);
wakable = NULL;
mw_sleep = SRS_PERF_MW_SLEEP;
mw_enabled = false;
realtime = SRS_PERF_MIN_LATENCY_ENABLED;
send_min_interval = 0;
tcp_nodelay = false;
client_type = SrsRtmpConnUnknown;
_srs_config->subscribe(this);
}
1.8.5.1 SrsRequest
SrsRequest 类定义,位于 srs_rtmp_stack.hpp:
/**
* the original request from client.
*/
class SrsRequest
{
public:
// client ip.
std::string ip;
public:
/**
* tcUrl: rtmp://request_vhost:port/app/stream
* support pass vhost in query string, such as:
* rtmp://ip:port/app?vhost=request_vhost/stream
* rtmp://ip:port/app...vhost...request_vhost/stream
*/
std::string tcUrl;
std::string pageUrl;
std::string swfUrl;
double objectEncoding;
// data discovery from request.
public:
// discovery from tcUrl and play/publish.
std::string schema;
// the vhost in tcUrl.
std::string vhost;
// the host in tcUrl.
std::string host;
// the port in tcUrl.
std::string port;
// the app in tcUrl, without param.
std::string app;
// the param in tcUrl(app).
std::string param;
// the stream in play/publish
std::string stream;
// for play live stream,
// used to specified the stop when exceed the duration.
// @see https://github.com/ossrs/srs/issues/45
// in ms.
double duration;
// the token in the connect request,
// used for edge traverse to origin authentication,
// @see https://github.com/ossrs/srs/issues/104
SrsAmf0Object* args;
public:
SrsRequest();
virtual ~SrsRequest();
public:
/**
* deep copy the request, for source to use it to support reload,
* for when initialize the source, the request is valid,
* when reload it, the request maybe invalid, so need to copy it.
*/
virtual SrsRequest* copy();
/**
* update the auth info of request,
* to keep the current request ptr is ok,
* for many components use the ptr of request.
*/
virtual void update_auth(SrsRequest* req);
/**
* get the stream identify, vhost/app/stream.
*/
virtual std::string get_stream_url();
/**
* strip url, user must strip when update the url.
*/
virtual void strip();
};
SrsRequest 的构造函数:
#define RTMP_SIG_AMF0_VER 0
SrsRequest::SrsRequest()
{
/* 初始化编码对象为 AMF0 */
objectEncoding = RTMP_SIG_AMF0_VER;
duration = -1;
args = NULL;
}
1.8.5.2 SrsResponse
SrsResponse 类定义,位于 srs_rtmp_stack.hpp:
/**
* the response to client.
*/
class SrsResponse
SrsResponse 类定义,位于 srs_rtmp_stack.hpp:
{
public:
/**
* the stream id to response client createStream.
*/
int stream_id;
public:
SrsResponse();
virtual ~SrsResponse();
};
SrsResponse 类构造函数,位于 srs_rtmp_stack.cpp:
// default stream id for response the createStream request.
#define SRS_DEFAULT_SID 1
SrsResponse::SrsResponse()
{
/* 该 stream_id 用于响应客户端的 createStream 命令时使用的流 id */
stream_id = SRS_DEFAULT_SID;
}
1.8.5.3 SrsStSocket
SrsStSocket 类定义,位于 srs_app_st.hpp:
/**
* the socket provides TCP socket over st,
* that is, the sync socket mechanism.
*/
class SrsStSocket : public ISrsProtocolReaderWriter
{
private:
int64_t recv_timeout;
int64_t send_timeout;
int64_t recv_bytes;
int64_t send_bytes;
st_netfd_t stfd;
public:
SrsStSocket(st_netfd_t client_stfd);
virtual ~SrsStSocket();
public:
virtual bool is_never_timeout(int64_t timeout_us);
virtual void set_recv_timeout(int64_t timeout_us);
virtual int64_t get_recv_timeout();
virtual void set_send_timeout(int64_t timeout_us);
virtual int64_t get_send_timeout();
virtual int64_t get_recv_bytes();
virtual int64_t get_send_bytes();
public:
/**
* @param nread, the actual read bytes, ignore if NULL.
*/
virtual int read(void* buf, size_t size, ssize_t* nread);
virtual int read_fully(void* buf, size_t size, ssize_t* nread);
/**
* @param nwrite, the actual write bytes, ignore if NULL.
*/
virtual int write(void* buf, size_t size, ssize_t* nwrite);
virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite);
};
SrsStSocket 构造函数,位于 srs_app_st.cpp:
#define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL)
SrsStSocket::SrsStSocket(st_netfd_t client_stfd)
{
/* client_stfd 保存已连接套接字描述符的信息 */
stfd = client_stfd;
/* 设置发送 rtmp 包时的超时时间,这里初始值为 -1,即一直等待,直到发送或接受成功 */
send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT;
/* 这两个分别是接受或发送字节数的统计值 */
recv_bytes = send_bytes = 0;
}
1.8.5.4 SrsRtmpServer
SrsRtmpServer 类定义,位于 srs_rtmp_stack.hpp:
/**
* the rtmp provices rtmp-command-protocol services,
* a high level protocol, media stream oriented services,
* such as connect to vhost/app, play stream, get audio/video data.
*/
class SrsRtmpServer
{
private:
SrsHandshakeBytes* hs_bytes;
SrsProtocol* protocol;
ISrsProtocolReaderWriter* io;
public:
SrsRtmpServer(ISrsProtocolReaderWriter* skt);
virtual ~SrsRtmpServer();
// protocol methods proxy
public:
/**
* set the auto response message when rece for protocol stack.
* @param v, whether auto response message when rece message.
* @see: https://github.com/ossrs/srs/issues/217
*/
virtual void set_auto_response(bool v);
#ifdef SRS_PERF_MERGED_READ
/**
* to improve read performance, merge some packets then read,
* when it on and read small bytes, we sleep to wait more data,
* that is, we merge some data to read together.
* @param v true to enable merged read.
* @param handler the handler when merge read is enabled.
* @see https://github.com/ossrs/srs/issues/241
*/
virtual void set_merge_read(bool v, IMergeReadHandler* handlers);
/**
* create buffer with specified size.
* @param buffer the size of buffer.
* @remark when MR(SRS_PERF_MERGED_READ) disabled, always set to 8K.
* @remark when buffer changed, the previous ptr maybe invalid.
* @see https://github.com/ossrs/srs/issues/241
*/
virtual void set_recv_buffer(int buffer_size);
#endif
/**
* set/get the recv timeout in us.
* if timeout, recv/send message return ERROR_SOCKET_TIMEOUT.
*/
virtual void set_recv_timeout(int64_t timeout_us);
virtual int64_t get_recv_timeout();
/**
* set/get the send timeout in us.
* if timeout, recv/send message return ERROR_SOCKET_TIMEOUT.
*/
virtual void set_send_timeout(int64_t timeout_us);
virtual int64_t get_send_timeout();
/**
* get recv/send bytes.
*/
virtual int64_t get_recv_bytes();
virtual int64_t get_send_bytes();
/**
* recv a RTMP message, which is bytes oriented.
* user can use decode_message to get the decoded RTMP packet.
* @param pmsg, set the received message
* always NULL if error,
* NULL for unknown packet but return success.
* never NULL if decode seccess.
* @remark, drop message when msg is empty or payload length is empty.
*/
virtual int recv_message(SrsCommonMessage** pmsg);
/**
* decode bytes oriented RTMP message to RTMP packet,
* @param ppacket, output decoded packet,
* always NULL if error, never NULL if success.
* @return error when unknown packet, error when decode failed.
*/
virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket);
/**
* send the RTMP message and always free it.
* user must never free or use the msg after this method,
* for it will always free the msg.
* @param msg, the msg to send out, never be NULL.
* @param stream_id, the stream id of packet to send over, 0 for control message.
*/
virtual int send_and_free_message(SrsSharedPtrMessage* msg, int stream_id);
/**
* send the RTMP message and always free it.
* user must never free or use the msg after this method,
* for it will always free the msg.
* @param msgs, the msgs to send out, never be NULL.
* @param nb_msgs, the size of msgs to send out.
* @param stream_id, the stream id of packet to send over, 0 for control message.
*
* @remark performance issue, to support 6k+ 250kbps client,
* @see https://github.com/ossrs/srs/issues/194
*/
virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs,
int stream_id);
/**
* send the RTMP packet and always free it.
* user must never free or use the packet after this method,
* for it will always free the packet.
* @param packet, the packet to send out, never be NULL.
* @param stream_id, the stream id of packet to send over, 0 for control message.
*/
virtual int send_and_free_packet(SrsPacket* packet, int stream_id);
public:
/**
* handshake with client, try complex then simple.
*/
virtual int handshake();
/**
* do connect app with client, to discovery tcUrl.
*/
virtual int connect_app(SrsRequest* req);
/**
* set ack size to client, client will send ack-size for each ack window
*/
virtual int set_window_ack_size(int ack_size);
/**
* @type: The sender can mark this message hard (0), soft (1), or dynamic (2)
* using the Limit type field.
*/
virtual int set_peer_bandwidth(int bandwidth, int type);
/**
* @param server_ip the ip of server.
*/
virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL);
/**
* reject the connect app request.
*/
virtual void response_connect_reject(SrsRequest* req, const char* desc);
/**
* response client the onBWDone message.
*/
virtual int on_bw_done();
/**
* recv some message to identify the client.
* @stream_id, client will createStream to play or publish by flash,
* the stream_id used to response the createStream request.
* @type, output the client type.
* @stream_name, output the client publish/play stream name. @see: SrsRequest.stream
* @duration, output the play client duration. @see: SrsRequest.duration
*/
virtual int identify_client(int stream_id, SrsRtmpConnType& type,
std::string& stream_name, double& duration);
/**
* set the chunk size when client type identified.
*/
virtual int set_chunk_size(int chunk_size);
/**
* when client type is play, response with packets:
* StreamBegin,
* onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start).,
* |RtmpSampleAccess(false, false),
* onStatus(NetStream.Data.Start).
*/
virtual int start_play(int stream_id);
/**
* when client(type is play) send pause message,
* if is_pause, response the following packets:
* onStatus(NetStream.Pause.Notify)
* StreamEOF
* if not is_pause, response the following packets:
* onStatus(NetStream.Unpause.Notify)
* StreamBegin
*/
virtual int on_play_client_pause(int stream_id, bool is_pause);
/**
* when client type is publish, response with packets:
* releaseStream response
* FCPublish
* FCPublish response
* createStream response
* onFCPublish(NetStream.Publish.Start)
* onStatus(NetStream.Publish.Start)
*/
virtual int start_fmle_publish(int stream_id);
/**
* For encoder of Haivision, response the startup request.
* @see https://github.com/ossrs/srs/issues/844
*/
virtual int start_haivision_publish(int stream_id);
/**
* process the FMLE unpublish event.
* @unpublish_tid the unpublish request transaction id.
*/
virtual int fmle_unpublish(int stream_id, double unpublish_tid);
/**
* when client type is publish, response with packets:
* onStatus(NetStream.Publish.Start)
*/
virtual int start_flash_publish(int stream_id);
public:
/**
* expect a specified message, drop others util got specified one.
* @pmsg, user must free it. NULL if not success.
* @ppacket, user must free it, which decode from payload of message.
* NULL if not success.
* @remark, only when success, user can use and must free the pmsg and ppacket.
* for example:
* SrsCommonMessage* msg = NULL;
* SrsConnectAppResPacket* pkt = NULL;
* if ((ret = server->expect_message<SrsConnectAppResPacket>(protocol,
* &msg, &pkt))
* != ERROR_SUCCESS) {
* return ret;
* }
* // use then free msg and pkt
* srs_freep(msg);
* srs_freep(pkt);
* user should never recv message and convert it, use this method instead.
* if need to set timeout, use set timeout of SrsProtocol.
*/
template<class T>
int expect_message(SrsCommonMessage** pmsg, T** ppacket)
{
return protocol->expect_message<T>(pmsg, ppacket);
}
private:
virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id,
SrsRtmpConnType& type, std::string& stream_name, double& duration);
virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req,
SrsRtmpConnType& type, std::string& stream_name);
virtual int identify_haivision_publish_client(SrsFMLEStartPacket* req,
SrsRtmpConnType& type, std::string& stream_name);
virtual int identify_flash_publish_client(SrsPublishPacket* req,
SrsRtmpConnType& type, std::string& stream_name);
private:
virtual int identify_play_client(SrsPlayPacket* req, SrsRtmpConnType& type,
std::string& stream_name, double& duration);
};
SrsRtmpServer 构造函数,位于 srs_rtmp_stack.cpp:
SrsRtmpServer::SrsRtmpServer(ISrsProtocolReaderWriter* skt)
{
/* io 是指向 ISrsProtocolReaderWriter 类对象的指针,skt 是传入的
* 指向刚构造的 SrsStSocket 类对象的指针, ISrsProtocolReaderWriter 是
* SrsStSocket 的父类 */
io = skt;
/* 构造 SrsProtocol 类, 该类用于提供 rtmp 消息协议的服务,
* 如从 rtmp 块流中接收 rtmp 消息,或者通过 rtmp 块流发送
* rtmp 消息*/
protocol = new SrsProtocol(skt);
/* 构造 SrsHandshakeBytes 类,该类用于读取或构建 handshake 过程的
* 数据,并保存 */
hs_bytes = new SrsHandshakeBytes();
}
1.8.6 SrsRtmpConn 构造总结
- 首先调用 SrsRtmpConn 的父类 SrsConnection 的构造函数,在该构造函数中,为其 SrsOneCycleThread 类的成员指针 pthread 构造了 SrsOneCycleThread 对象,在 SrsOneCycleThread 构造函数中,又接着为其 internal::SrsThread 类的成员 pthread 构造了 internal::SrsThread 对象。SrsOneCycleThread 是一个只循环一次的线程,当从循环中返回时便终止该线程。具体创建步骤可参考 ISrsOneCycleThreadHandler 类的说明。
- 接着调用 SrsRtmpConn 的构造函数,在该构造函数中,为其成员 req、res、skt、rtmp、refer、bandwidth、security 等类对象的指针构造了新的类对象,分别对应 SrsRequest、SrsResponse、SrsStSocket、SrsRtmpServer、SrsRefer、SrsBandwidth、SrsSecurity。
1.9 启动 conn 线程:SrsConnection::start
位于 srs_app_conn.cpp 中:
int SrsConnection::start()
{
/* pthread 是指向 SrsOneCycleThread 类的指针,因此调用该类的成员函数
* start 函数 */
return pthread->start();
}
int SrsOneCycleThread::start()
{
/* pthread 是 internal::SrsThread 类的指针,因此接着调用该类的成员函数 start */
return pthread->start();
}
int SrsThread::start()
{
int ret = ERROR_SUCCESS;
if(tid) {
srs_info("thread %s already running.", _name);
return ret;
}
if((tid = st_thread_create(thread_fun, this, (_joinable? 1:0), 0)) == NULL){
ret = ERROR_ST_CREATE_CYCLE_THREAD;
srs_error("st_thread_create failed. ret=%d", ret);
return ret;
}
disposed = false;
// we set to loop to true for thread to run.
loop = true;
// wait for cid to ready, for parent thread to get the cid.
while (_cid < 0) {
st_usleep(10 * 1000);
}
// now, cycle thread can run.
can_run = true;
return ret;
}
从 SrsConnection::start 开始,一路层层调用下来,最终调用 SrsThread::start() 函数,在该函数中会调用 st_thread_create 创建一个线程,然后进入休眠等待 _cid 大于 0,才会再次继续往下运行。
致此,该 RTMP的TCP线程的 cycle 循环完成一次,一路返回到 SrsThread::thread_cycle() 函数中调用 handler->cycle() 的地方继续往下执行。
1.10 SrsReusableThread::on_end_cycle
int SrsReusableThread::on_end_cycle()
{
/* handler 是 ISrsReusableThreadHandler 类的指针,由开篇的 handler 关系图知,
* 这里调用的是 ISrsReusableThreadHandler 的 on_end_cycle 函数 */
return handler->on_end_cycle();
}
int ISrsReusableThreadHandler::on_end_cycle()
{
return ERROR_SUCCESS;
}
3. 总结
整个 RTMP 的 TCP 线程的大致流程是:
- 在线程循环中调用 ST 的库函数 st_accept 接受客户端的连接请求,若当前没有连接请求则将当前线程添加到 IO 队列中,然后将控制权让出去,调度其他线程运行。直到有客户端的连接请求到来后才再次调度;
- 接受客户端的连接请求后,根据客户端连接的类型:RTMP 或 HTTP api 等构建相应的 connection 类,然后添加到 SrsServe 类下的 conns 容器中,该容器保存着所有的连接;
- 接着为该连接创建一个线程,如对于 RTMP,创建的线程是 SrsOneCycleThread 类的,该类型的线程是只循环一次就结束该线程;
- 同时在 SrsRtmpConn 的构造函数中也构建了多个类对象,这些对象用于为服务器与客户端之间的 RTMP 通信提供服务,如 SrsRtmpServer 类对象。
- 该 RTMP 的 TCP 线程循环一次后即代表建立了一个连接,接着休眠后再次循环接受下一个连接请求。