1. 概述

监听端口属于 server 虚拟主机,它是由 server{} 块下的 listen 配置项决定的。

每监听一个 TCP 端口,都将使用一个独立的 ngx_rtmp_conf_port_t 结构体来表示:

typedef struct {
/* socket 地址家族 */
int family; /* 监听端口 */
in_port_t port; /* 监听的端口下对应着的所有 ngx_rtmp_conf_addr_t 地址 */
ngx_array_t addrs; /* array of ngx_rtmp_conf_addr_t */
} ngx_rtmp_conf_port_t;

一个端口,可能对应着多个地址(当主机上有多个 IP 地址时),该地址用 ngx_rtmp_conf_addr_t 表示:

typedef struct {
struct sockaddr *sockaddr;
socklen_t socklen; ngx_rtmp_conf_ctx_t *ctx; unsigned bind:1;
unsigned wildcard:1;
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
unsigned ipv6only:2;
#endif
unsigned so_keepalive:2;
unsigned proxy_protocol:1;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
int tcp_keepidle;
int tcp_keepintvl;
int tcp_keepcnt;
#endif
} ngx_rtmp_conf_addr_t;

在 ngx_rtmp_core_main_conf_t 结构体中,保存着 nginx.conf 中所有需要监听的端口:

typedef struct {
...
/* 该数组保存着的元素是指向 ngx_rtmp_listen_t 结构体的指针,
* 每个元素代表一个需要监听的端口 */
ngx_array_t listen; /* ngx_rtmp_listen_t */
...
}ngx_rtmp_core_main_conf_t;

Nginx 每从 nginx.conf 中解析中一个 listen 配置项,就把该配置项的值组织成 ngx_rtmp_listen_t 结构体添加到

ngx_rtmp_core_main_conf_t 的成员 listen 数组中,如下:

typedef struct {
u_char sockaddr[NGX_SOCKADDRLEN];
socklen_t socklen; /* server ctx */
ngx_rtmp_conf_ctx_t *ctx; unsigned bind:1;
unsigned wildcard:1;
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
unsigned ipv6only:2;
#endif
unsigned so_keepalive:2;
unsigned proxy_protocol:1;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
int tcp_keepidle;
int tcp_keepintvl;
int tcp_keepcnt;
#endif
} ngx_rtmp_listen_t;

对同一个端口 1935,可以同时监听 127.0.0.1:8000、192.168.56.101:1935 这两个地址,当一台物理机器具备多个 IP

地址时这是很有用的。对应到 RTMP 的框架上,Nginx 是使用 ngx_rtmp_conf_addr_t 结构体来表示一个对应着具体地址

的监听端口,因此,一个 ngx_rtmp_conf_port_t 将会对应多个 ngx_rtmp_conf_addr_t,而 ngx_rtmp_conf_addr_t 是

以动态数组的形式保存在 ngx_rtmp_conf_port_t 中的 addrs 成员中的。

2. 监听端口的解析与管理

2.1 ngx_rtmp_core_listen:解析 listen 配置项

static char *ngx_rtmp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
size_t len, off;
in_port_t port;
ngx_str_t *value;
ngx_url_t u;
ngx_uint_t i, m;
ngx_module_t **modules;
struct sockaddr *sa;
ngx_rtmp_listen_t *ls;
struct sockaddr_in *sin;
ngx_rtmp_core_main_conf_t *cmcf;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
#endif /* 指向 "listen xxx" 配置项,如 "listen 1935" */
value = cf->args->elts; ngx_memzero(&u, sizeof(ngx_url_t)); /* 将监听的端口赋给 u.url,如 "1935" */
u.url = value[1];
u.listen = 1; /* 解析该监听的端口 */
if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
if (u.err) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"%s in \"%V\" of the \"listen\" directive",
u.err, &u.url);
} return NGX_CONF_ERROR;
} cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module); ls = cmcf->listen.elts; /* 遍历listen数组,若有元素,将待监听的地址与数组中的保存的地址进行比较,
* 若有相同的,则返回错误,否则添加到listen数组中 */
for (i = 0; i < cmcf->listen.nelts; i++) { sa = (struct sockaddr *) ls[i].sockaddr; if (sa->sa_family != u.family) {
continue;
} switch (sa->sa_family) { #if (NGX_HAVE_INET6)
case AF_INET6:
off = offsetof(struct sockaddr_in6, sin6_addr);
len = 16;
sin6 = (struct sockaddr_in6 *) sa;
port = sin6->sin6_port;
break;
#endif default: /* AF_INET */
off = offsetof(struct sockaddr_in, sin_addr);
len = 4;
sin = (struct sockaddr_in *) sa;
port = sin->sin_port;
break;
} if (ngx_memcmp(ls[i].sockaddr + off, (u_char *) &u.sockaddr + off, len)
!= 0)
{
continue;
} if (port != u.port) {
continue;
} ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate \"%V\" address and port pair", &u.url);
return NGX_CONF_ERROR;
} ls = ngx_array_push(&cmcf->listen);
if (ls == NULL) {
return NGX_CONF_ERROR;
} ngx_memzero(ls, sizeof(ngx_rtmp_listen_t)); ngx_memcpy(ls->sockaddr, (u_char *) &u.sockaddr, u.socklen); ls->socklen = u.socklen;
ls->wildcard = u.wildcard;
ls->ctx = cf->ctx; #if (nginx_version >= 1009011)
modules = cf->cycle->modules;
#else
modules = ngx_modules;
#endif for (m = 0; modules[m]; m++) {
if (modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
} /* 若监听端口后面还附加有其他条件,则进行处理,否则直接返回 */
for (i = 2; i < cf->args->nelts; i++) { if (ngx_strcmp(value[i].data, "bind") == 0) {
ls->bind = 1;
continue;
} if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
struct sockaddr *sa;
u_char buf[NGX_SOCKADDR_STRLEN]; sa = (struct sockaddr *) ls->sockaddr; if (sa->sa_family == AF_INET6) { if (ngx_strcmp(&value[i].data[10], "n") == 0) {
ls->ipv6only = 1; } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
ls->ipv6only = 0; } else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid ipv6only flags \"%s\"",
&value[i].data[9]);
return NGX_CONF_ERROR;
} ls->bind = 1; } else {
len = ngx_sock_ntop(sa,
#if (nginx_version >= 1005003)
ls->socklen,
#endif
buf, NGX_SOCKADDR_STRLEN, 1); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"ipv6only is not supported "
"on addr \"%*s\", ignored", len, buf);
} continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"bind ipv6only is not supported "
"on this platform");
return NGX_CONF_ERROR;
#endif
} if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) { if (ngx_strcmp(&value[i].data[13], "on") == 0) {
ls->so_keepalive = 1; } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
ls->so_keepalive = 2; } else { #if (NGX_HAVE_KEEPALIVE_TUNABLE)
u_char *p, *end;
ngx_str_t s; end = value[i].data + value[i].len;
s.data = value[i].data + 13; p = ngx_strlchr(s.data, end, ':');
if (p == NULL) {
p = end;
} if (p > s.data) {
s.len = p - s.data; ls->tcp_keepidle = ngx_parse_time(&s, 1);
if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
goto invalid_so_keepalive;
}
} s.data = (p < end) ? (p + 1) : end; p = ngx_strlchr(s.data, end, ':');
if (p == NULL) {
p = end;
} if (p > s.data) {
s.len = p - s.data; ls->tcp_keepintvl = ngx_parse_time(&s, 1);
if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
goto invalid_so_keepalive;
}
} s.data = (p < end) ? (p + 1) : end; if (s.data < end) {
s.len = end - s.data; ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
if (ls->tcp_keepcnt == NGX_ERROR) {
goto invalid_so_keepalive;
}
} if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
&& ls->tcp_keepcnt == 0)
{
goto invalid_so_keepalive;
} ls->so_keepalive = 1; #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"so_keepalive\" parameter accepts "
"only \"on\" or \"off\" on this platform");
return NGX_CONF_ERROR; #endif
} ls->bind = 1; continue; #if (NGX_HAVE_KEEPALIVE_TUNABLE)
invalid_so_keepalive: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid so_keepalive value: \"%s\"",
&value[i].data[13]);
return NGX_CONF_ERROR;
#endif
} if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) {
ls->proxy_protocol = 1;
continue;
} ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the invalid \"%V\" parameter", &value[i]);
return NGX_CONF_ERROR;
} return NGX_CONF_OK;
}

2.2 ngx_rtmp_add_ports: 将监听端口添加到 ports 数组中

static ngx_int_t ngx_rtmp_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
ngx_rtmp_listen_t *listen)
{
in_port_t p;
ngx_uint_t i;
struct sockaddr *sa;
struct sockaddr_in *sin;
ngx_rtmp_conf_port_t *port;
ngx_rtmp_conf_addr_t *addr;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
#endif sa = (struct sockaddr *) &listen->sockaddr; switch (sa->sa_family) { #if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) sa;
p = sin6->sin6_port;
break;
#endif default: /* AF_INET */
sin = (struct sockaddr_in *) sa;
p = sin->sin_port;
break;
} /* 首先比较 ports 数组中是否已经存在该待监听端口的,若已经存在,
* 则不用再次添加了,直接跳到 found 下,否则添加一个新的端口 */
port = ports->elts;
for (i = 0; i < ports->nelts; i++) {
if (p == port[i].port && sa->sa_family == port[i].family) { /* a port is already in the port list */ port = &port[i];
goto found;
}
} /* add a port to the port list */ port = ngx_array_push(ports);
if (port == NULL) {
return NGX_ERROR;
} port->family = sa->sa_family;
port->port = p; if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
sizeof(ngx_rtmp_conf_addr_t))
!= NGX_OK)
{
return NGX_ERROR;
} found: /* 这里有可能一个端口对应几个 IP 地址 */
addr = ngx_array_push(&port->addrs);
if (addr == NULL) {
return NGX_ERROR;
} addr->sockaddr = (struct sockaddr *) &listen->sockaddr;
addr->socklen = listen->socklen;
addr->ctx = listen->ctx;
addr->bind = listen->bind;
addr->wildcard = listen->wildcard;
addr->so_keepalive = listen->so_keepalive;
addr->proxy_protocol = listen->proxy_protocol;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
addr->tcp_keepidle = listen->tcp_keepidle;
addr->tcp_keepintvl = listen->tcp_keepintvl;
addr->tcp_keepcnt = listen->tcp_keepcnt;
#endif
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
addr->ipv6only = listen->ipv6only;
#endif return NGX_OK;
}

2.3 ngx_rtmp_optimize_servers

static char *ngx_rtmp_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
{
ngx_uint_t i, p, last, bind_wildcard;
ngx_listening_t *ls;
ngx_rtmp_port_t *mport;
ngx_rtmp_conf_port_t *port;
ngx_rtmp_conf_addr_t *addr; port = ports->elts;
for (p = 0; p < ports->nelts; p++) { ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
sizeof(ngx_rtmp_conf_addr_t), ngx_rtmp_cmp_conf_addrs); addr = port[p].addrs.elts;
last = port[p].addrs.nelts; /*
* if there is the binding to the "*:port" then we need to bind()
* to the "*:port" only and ignore the other bindings
*/ if (addr[last - 1].wildcard) {
addr[last - 1].bind = 1;
bind_wildcard = 1; } else {
bind_wildcard = 0;
} i = 0; while (i < last) { if (bind_wildcard && !addr[i].bind) {
i++;
continue;
} /* 创建并初始化 ngx_listening_t 结构体 */
ls = ngx_create_listening(cf, addr[i].sockaddr, addr[i].socklen);
if (ls == NULL) {
return NGX_CONF_ERROR;
} ls->addr_ntop = 1;
ls->handler = ngx_rtmp_init_connection;
ls->pool_size = 4096; /* TODO: error_log directive */
ls->logp = &cf->cycle->new_log;
ls->log.data = &ls->addr_text;
ls->log.handler = ngx_accept_log_error; ls->keepalive = addr[i].so_keepalive;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
ls->keepidle = addr[i].tcp_keepidle;
ls->keepintvl = addr[i].tcp_keepintvl;
ls->keepcnt = addr[i].tcp_keepcnt;
#endif #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
ls->ipv6only = addr[i].ipv6only;
#endif mport = ngx_palloc(cf->pool, sizeof(ngx_rtmp_port_t));
if (mport == NULL) {
return NGX_CONF_ERROR;
} ls->servers = mport; if (i == last - 1) {
mport->naddrs = last; } else {
mport->naddrs = 1;
i = 0;
} switch (ls->sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
if (ngx_rtmp_add_addrs6(cf, mport, addr) != NGX_OK) {
return NGX_CONF_ERROR;
}
break;
#endif
default: /* AF_INET */
if (ngx_rtmp_add_addrs(cf, mport, addr) != NGX_OK) {
return NGX_CONF_ERROR;
}
break;
} addr++;
last--;
}
} return NGX_CONF_OK;
}

Nginx-rtmp之监听端口的管理的更多相关文章

  1. SRS之监听端口的管理:RTMP

    1. 监听端口管理的入口函数 监听端口的管理入口在 run_master 函数中,如下: int run_master() { ... if ((ret = _srs_server->liste ...

  2. Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差

    Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差别   Nginx连接fastcgi的方式有2种:unix domain socket和TCP,Uni ...

  3. nginx监听端口和反向代理端口的权限问题

    Linux的SELinux安全性控制除作用于文件系统外还作用于端口,这使得那些作为服务启动的进程只能在规定的几个端口上监听.为叙述方便我们称之为受控端口. nginx监听端口 要查看当前有哪些受控端口 ...

  4. 修改Oracle监听端口

    修改oracle监听端口 修改端口号的整体步骤:1.1   查看当前监听的状态1.2   停止监听1.3   修改监听文件的端口号1.4   修改初始化参数local_listener1.5   重启 ...

  5. Windows下修改Oracle监听端口

    先放开防火墙的端口,再来改监听端口. 只有一个实例: net stop OracleOraDb11g_home1TNSListener 再运行 NET Manager 修改实例名与Listener的端 ...

  6. Oracle RAC 11gR2 修改本地及SCAN监听端口

        昨天同事说有套RAC集群客户要求修改数据库的监听端口,在处理的过程中,发现网上的相关资料都不是很全面,所以整理了一下,希望给其他有需要的朋友提供一点有利的参考资料.具体操作过程如下:     ...

  7. Apache配置多个监听端口和不同的网站目录的简单方法(转)

    转自http://www.waaqi.com/archives/707.html 由于开发的多项目,每个项目又要独立,要用根目录地址. 所以这时候我们需要配置多个不同目录的Apache,如果是外部网可 ...

  8. .net core Kestrel宿主服务器自定义监听端口配置

    在.net core的web程序中,除了可以在项目中硬编码服务器的监听端口外,还可以在外部通过json文件配置. 方法如下: 第一步:在项目中新建一个名为Hosting.json的文件.当然,文件名可 ...

  9. 黄聪:windows下使用xampp3.2.2配置多个监听端口和不同的网站目录

    windows下使用xampp3.2.2配置多个监听端口和不同的网站目录 一:配置Apache文件httpd.conf 打开Apache的配置文件httpd.conf,可以通过点击xampp的Apac ...

随机推荐

  1. Centos7:Redis的安装,配置及使用

    安装依赖与环境 yum install gcc-c++ 解压缩redis 编译,进入redis源码目录 make 安装 make install PREFIX=/usr/local/redis 注:P ...

  2. js之数据类型(对象类型——构造器对象——函数1)

    函数它只定义一次,但可能被多次的执行和调用.JavaScript函数是参数化的,函数的定义会包括形参和实参.形参相当于函数中定义的变量,实参是在运行函数调用时传入的参数. 一.函数定义 函数使用fun ...

  3. js页面加载时候的调用函数的方法

    方法一:jquery 中:$(function(){}) 括号内写你的内容 方法二:html <body onload=''> <script type="text/jav ...

  4. Nginx作为静态资源web服务之防盗链

    Nginx作为静态资源web服务之防盗链 首先,为什么需要防盗链,因为有些资源存在竞争对手的关系,比如淘宝的商品图片,不会轻易的让工具来爬虫爬走收集.但是如果使用防盗链,需要知道上一个访问的资源,然后 ...

  5. OpenResty 执行流程阶段

    nginx有11个处理阶段,如下图所示: 指令 所处处理阶段 使用范围 解释 init_by_luainit_by_lua_file loading-config http nginx Master进 ...

  6. In Unix, what is tar, and how do I use it?

      In Unix, the name of the tar command is short for tape archiving, the storing of entire file syste ...

  7. 使用 Eclipse 构建的时候会出现 run as 中没有 maven package 选项

    注:该方法来自我学习时别人分享的出现问题的解决方法,并没有亲自测试,仅供参考 是因为建的是普通 java 工程,需要把它转换成 maven project. 1.右键工程--maven--Disabl ...

  8. 白话跨域CORS

    跨域访问控制是浏览器和服务器按照约定,协同工作,守护安全的一种机制. 其中认为浏览器和服务器是安全的,但是浏览器上运行的页面(HTML+JS)可能不安全. 分几种不同方式. 页面跨域简单请求(Get/ ...

  9. 【算法学习笔记】RMQ问题与ST表

    \(0.\) RMQ问题 P1816 人话翻译 给定一个长度为\(n\)的数列\(a\),然后有\(m\)组询问,每次询问一个区间\([l,r]\)的最小值. 其中\(m,n\leq10^5\) \( ...

  10. MySQL 关于触发器的操作

    -- 监视某种情况,并触发某种操作 --触发器创建语法四要素 1.监视地点 2.监视事件 3.触发时间 4.触发事件 -- 触发器只能创建在永久表上,不能对临时表创建触发器 create trigge ...