上一节,我们介绍了rtmp模块的初始化;在上一节中,我们提到了,当有rtmp模块的请求到达时,nginx处理网络事件的event模块会回调rtmp模块的ngx_rtmp_init_connection接口,本节中,我们将分析一下RTMP模块是如何处理连接请求的。
我们首先看一下ngx_rtmp_init_connection该函数的函数体,在函数体部分有对关键部分的解释说明:
void
ngx_rtmp_init_connection(ngx_connection_t *c)
{
ngx_uint_t i;
ngx_rtmp_port_t *port;
struct sockaddr *sa;
struct sockaddr_in *sin;
ngx_rtmp_in_addr_t *addr;
ngx_rtmp_session_t *s;
ngx_rtmp_addr_conf_t *addr_conf;
ngx_int_t unix_socket;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
ngx_rtmp_in6_addr_t *addr6;
#endif
//该变量是全局变量,用来统计rtmp模块的请求总数
++ngx_rtmp_naccepted;
/* find the server configuration for the address:port */
/* AF_INET only */
//由于server域配置的是监听的端口,在多网卡机器上,会出现同一个端口port,有多个ip监听的情况,所以需要知道,具体请求时哪个ip地址上来的
port = c->listening->servers;
unix_socket = 0;
if (port->naddrs > 1) {
//当该端口port上,存在多个ip地址,也就是说多个ip地址在监听这个端口的情况
/*
* There are several addresses on this port and one of them
* is the "*:port" wildcard so getsockname() is needed to determine
* the server address.
*
* AcceptEx() already gave this address.
*/
//获取该处理该连接请求的本地IP地址,也就是监听的ip地址,这个地址可以更具fd获取到
if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
ngx_rtmp_close_connection(c);
return;
}
sa = c->local_sockaddr;
switch (sa->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) sa;
addr6 = port->addrs;
/* the last address is "*" */
for (i = 0; i < port->naddrs - 1; i++) {
if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
break;
}
}
addr_conf = &addr6[i].conf;
break;
#endif
case AF_UNIX:
unix_socket = 1;
/* fall through */
default: /* AF_INET */
sin = (struct sockaddr_in *) sa;
addr = port->addrs;
/* the last address is "*" */
//根据fd获取到的地址,匹配配置文件中配置的监听地址,以获取配置文件中关于该监听地址的配置信息
for (i = 0; i < port->naddrs - 1; i++) {
if (addr[i].addr == sin->sin_addr.s_addr) {
break;
}
}
addr_conf = &addr[i].conf;
break;
}
} else {
//当端口只被一个地址监听的情况,获取配置中的监听信息
switch (c->local_sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
addr6 = port->addrs;
addr_conf = &addr6[0].conf;
break;
#endif
case AF_UNIX:
unix_socket = 1;
/* fall through */
default: /* AF_INET */
addr = port->addrs;
addr_conf = &addr[0].conf;
break;
}
}
ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client connected '%V'",
c->number, &c->addr_text);
//当没有一个用户请求,rtmp模块都会为他创建一个会话的session,后续用户的操作,都是基于这个session尽情的,在这个地方去为用户的请求创建session
s = ngx_rtmp_init_session(c, addr_conf);
if (s == NULL) {
return;
}
/* only auto-pushed connections are
* done through unix socket */
//设置该session的auto_push标志为,只有本机通信的时候,才会时auto_push
s->auto_pushed = unix_socket;
//根据地址的配置信息,查看该地址时候时代理协议的地址,不同的监听地址,会进行不同的处理
if (addr_conf->proxy_protocol) {
ngx_rtmp_proxy_protocol(s);
} else {
//当该地址时rtmp真实服务的地址的话,就进行rtmp协议中的三次握手
ngx_rtmp_handshake(s);
}
}
上面给出了ngx_rtmp_init_connection函数处理用户连接请求的主要过程,在整个请求处理过程中,有以下几个重要的地方
1、获取监听的ip的相关配置信息
2、为用户请求创建session
3、针对于ip地址信息,做不同的处理
接下来我们看一下时如何为用户创建会话的session的
//从ngx_rtmp_init_connection函数中,我们可以知道,调用的两个参数,一个为用户请求的connection信息,一个为监听地址的配置信息
ngx_rtmp_session_t *ngx_rtmp_init_session(ngx_connection_t *c, ngx_rtmp_addr_conf_t *addr_conf)
{
ngx_rtmp_session_t *s;
ngx_rtmp_core_srv_conf_t *cscf;
ngx_rtmp_error_log_ctx_t *ctx;
//为session创建内存空间
s = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_session_t) +
sizeof(ngx_chain_t *) * ((ngx_rtmp_core_srv_conf_t *)
addr_conf->ctx-> srv_conf[ngx_rtmp_core_module
.ctx_index])->out_queue);
if (s == NULL) {
ngx_rtmp_close_connection(c);
return NULL;
}
//将配置文件rtmp{}域和所属的server{}域的信息拷贝到用户的session中
s->main_conf = addr_conf->ctx->main_conf;
s->srv_conf = addr_conf->ctx->srv_conf;
//将地址信息拷贝到session域中
s->addr_text = &addr_conf->addr_text;
//将session的地址存放到connection信息的data指针中,也可以理解为将session和connect绑定
c->data = s;
//指定session时属于哪个connection对象的
s->connection = c;
//为该用户连接分配日志存储空间并设置日志相关的信息
ctx = ngx_palloc(c->pool, sizeof(ngx_rtmp_error_log_ctx_t));
if (ctx == NULL) {
ngx_rtmp_close_connection(c);
return NULL;
}
ctx->client = &c->addr_text;
ctx->session = s;
c->log->connection = c->number;
c->log->handler = ngx_rtmp_log_error;
c->log->data = ctx;
c->log->action = NULL;
c->log_error = NGX_ERROR_INFO;
//为,欸一个模块分配一个指针数据空间,以备后用
s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_rtmp_max_module);
if (s->ctx == NULL) {
ngx_rtmp_close_connection(c);
return NULL;
}
//获取所在的server{}域的配置信息
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
//为session设置相关的参数信息,这些信息都是在server{}域配置信息中配置的
s->out_queue = cscf->out_queue;
s->out_cork = cscf->out_cork;
s->in_streams = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_stream_t)
* cscf->max_streams);
if (s->in_streams == NULL) {
ngx_rtmp_close_connection(c);
return NULL;
}
#if (nginx_version >= 1007005)
ngx_queue_init(&s->posted_dry_events);
#endif
//设置当前的时间
s->epoch = ngx_current_msec;
s->timeout = cscf->timeout;
s->buflen = cscf->buflen;
ngx_rtmp_set_chunk_size(s, NGX_RTMP_DEFAULT_CHUNK_SIZE);//设置当前默认的chunk的size大小,默认为128byte
//触发rtmp的connect事件
if (ngx_rtmp_fire_event(s, NGX_RTMP_CONNECT, NULL, NULL) != NGX_OK) {
ngx_rtmp_finalize_session(s);
return NULL;
}
//最后返回创建好的session
return s;
}
NGX_RTMP_CONNECT事件是在rtmp_limit模块中的,主要就是校验请求是否超过能够处理的上线的,后面我们在分模块处理的时候,将会做详细介绍;
这就是创建session的整个过程,执行完后,用户连接请求处理成功,并且为用户分配了一个session,后续用户的所有请求操作都是基于这个session进行
总结:
上面这个就是rtmp模块处理用户连接请求的过程,这个过程中主要就是为用户创建一个session,并将session和用户连接的connection对象关联起来