static char *
ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_srv_conf_t *cscf = conf;
ngx_str_t *value, size;
ngx_url_t u;
ngx_uint_t n, i;
ngx_http_listen_opt_t lsopt;
cscf->listen = 1;
value = cf->args->elts;
ngx_memzero(&u, sizeof(ngx_url_t));
u.url = value[1];
u.listen = 1;
u.default_port = 80;
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;
}
ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
lsopt.backlog = NGX_LISTEN_BACKLOG;
lsopt.rcvbuf = -1;
lsopt.sndbuf = -1;
#if (NGX_HAVE_SETFIB)
lsopt.setfib = -1;
#endif
#if (NGX_HAVE_TCP_FASTOPEN)
lsopt.fastopen = -1;
#endif
#if (NGX_HAVE_INET6)
lsopt.ipv6only = 1;
#endif
for (n = 2; n < cf->args->nelts; n++) {
if (ngx_strcmp(value[n].data, "default_server") == 0
|| ngx_strcmp(value[n].data, "default") == 0)
{
lsopt.default_server = 1;
continue;
}
if (ngx_strcmp(value[n].data, "bind") == 0) {
lsopt.set = 1;
lsopt.bind = 1;
continue;
}
#if (NGX_HAVE_SETFIB)
if (ngx_strncmp(value[n].data, "setfib=", 7) == 0) {
lsopt.setfib = ngx_atoi(value[n].data + 7, value[n].len - 7);
lsopt.set = 1;
lsopt.bind = 1;
if (lsopt.setfib == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid setfib \"%V\"", &value[n]);
return NGX_CONF_ERROR;
}
continue;
}
#endif
#if (NGX_HAVE_TCP_FASTOPEN)
if (ngx_strncmp(value[n].data, "fastopen=", 9) == 0) {
lsopt.fastopen = ngx_atoi(value[n].data + 9, value[n].len - 9);
lsopt.set = 1;
lsopt.bind = 1;
if (lsopt.fastopen == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid fastopen \"%V\"", &value[n]);
return NGX_CONF_ERROR;
}
continue;
}
#endif
if (ngx_strncmp(value[n].data, "backlog=", 8) == 0) {
lsopt.backlog = ngx_atoi(value[n].data + 8, value[n].len - 8);
lsopt.set = 1;
lsopt.bind = 1;
if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid backlog \"%V\"", &value[n]);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strncmp(value[n].data, "rcvbuf=", 7) == 0) {
size.len = value[n].len - 7;
size.data = value[n].data + 7;
lsopt.rcvbuf = ngx_parse_size(&size);
lsopt.set = 1;
lsopt.bind = 1;
if (lsopt.rcvbuf == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid rcvbuf \"%V\"", &value[n]);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strncmp(value[n].data, "sndbuf=", 7) == 0) {
size.len = value[n].len - 7;
size.data = value[n].data + 7;
lsopt.sndbuf = ngx_parse_size(&size);
lsopt.set = 1;
lsopt.bind = 1;
if (lsopt.sndbuf == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid sndbuf \"%V\"", &value[n]);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strncmp(value[n].data, "accept_filter=", 14) == 0) {
#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
lsopt.accept_filter = (char *) &value[n].data[14];
lsopt.set = 1;
lsopt.bind = 1;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"accept filters \"%V\" are not supported "
"on this platform, ignored",
&value[n]);
#endif
continue;
}
if (ngx_strcmp(value[n].data, "deferred") == 0) {
#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
lsopt.deferred_accept = 1;
lsopt.set = 1;
lsopt.bind = 1;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the deferred accept is not supported "
"on this platform, ignored");
#endif
continue;
}
if (ngx_strncmp(value[n].data, "ipv6only=o", 10) == 0) {
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
if (ngx_strcmp(&value[n].data[10], "n") == 0) {
lsopt.ipv6only = 1;
} else if (ngx_strcmp(&value[n].data[10], "ff") == 0) {
lsopt.ipv6only = 0;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid ipv6only flags \"%s\"",
&value[n].data[9]);
return NGX_CONF_ERROR;
}
lsopt.set = 1;
lsopt.bind = 1;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"ipv6only is not supported "
"on this platform");
return NGX_CONF_ERROR;
#endif
}
if (ngx_strcmp(value[n].data, "reuseport") == 0) {
#if (NGX_HAVE_REUSEPORT)
lsopt.reuseport = 1;
lsopt.set = 1;
lsopt.bind = 1;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"reuseport is not supported "
"on this platform, ignored");
#endif
continue;
}
if (ngx_strcmp(value[n].data, "ssl") == 0) {
#if (NGX_HTTP_SSL)
lsopt.ssl = 1;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"ssl\" parameter requires "
"ngx_http_ssl_module");
return NGX_CONF_ERROR;
#endif
}
if (ngx_strcmp(value[n].data, "http2") == 0) {
#if (NGX_HTTP_V2)
lsopt.http2 = 1;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"http2\" parameter requires "
"ngx_http_v2_module");
return NGX_CONF_ERROR;
#endif
}
if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) {
if (ngx_strcmp(&value[n].data[13], "on") == 0) {
lsopt.so_keepalive = 1;
} else if (ngx_strcmp(&value[n].data[13], "off") == 0) {
lsopt.so_keepalive = 2;
} else {
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
u_char *p, *end;
ngx_str_t s;
end = value[n].data + value[n].len;
s.data = value[n].data + 13;
p = ngx_strlchr(s.data, end, ':');
if (p == NULL) {
p = end;
}
if (p > s.data) {
s.len = p - s.data;
lsopt.tcp_keepidle = ngx_parse_time(&s, 1);
if (lsopt.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;
lsopt.tcp_keepintvl = ngx_parse_time(&s, 1);
if (lsopt.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;
lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len);
if (lsopt.tcp_keepcnt == NGX_ERROR) {
goto invalid_so_keepalive;
}
}
if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0
&& lsopt.tcp_keepcnt == 0)
{
goto invalid_so_keepalive;
}
lsopt.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
}
lsopt.set = 1;
lsopt.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[n].data[13]);
return NGX_CONF_ERROR;
#endif
}
if (ngx_strcmp(value[n].data, "proxy_protocol") == 0) {
lsopt.proxy_protocol = 1;
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[n]);
return NGX_CONF_ERROR;
}
for (n = 0; n < u.naddrs; n++) {
for (i = 0; i < n; i++) {
if (ngx_cmp_sockaddr(u.addrs[n].sockaddr, u.addrs[n].socklen,
u.addrs[i].sockaddr, u.addrs[i].socklen, 1)
== NGX_OK)
{
goto next;
}
}
lsopt.sockaddr = u.addrs[n].sockaddr;
lsopt.socklen = u.addrs[n].socklen;
lsopt.addr_text = u.addrs[n].name;
lsopt.wildcard = ngx_inet_wildcard(lsopt.sockaddr);
if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {
return NGX_CONF_ERROR;
}
next:
continue;
}
return NGX_CONF_OK;
}
ngx_http_core_listen
函数是 Nginx 核心模块中用于解析和处理 HTTP 服务器配置块中 listen
指令的关键函数
cscf->listen = 1;
标记当前服务器配置块(
server
块)已配置listen
指令ngx_http_core_srv_conf_t-CSDN博客
value = cf->args->elts;
获取当前配置指令(
listen
)的参数列表
ngx_memzero(&u, sizeof(ngx_url_t));
初始化
ngx_url_t
结构体u
,清空其内存ngx_url_t-CSDN博客
u.url = value[1];
u.listen = 1;
u.default_port = 80;
将用户配置的监听地址赋值给
ngx_url_t
结构体的url
字段标记当前 URL 是用于监听套接字(而非普通 URL)
设置监听地址的默认端口为
80
此时
value[1].data=80
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;
}
调用
ngx_parse_url
解析用户配置的监听地址(如127.0.0.1:8080
),并检查是否成功
ngx_parse_url
ngx_parse_url-CSDN博客
ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
将
ngx_http_listen_opt_t
类型的变量lsopt
的内存空间全部置零,确保所有字段初始值为0
ngx_http_listen_opt_t-CSDN博客
lsopt.backlog = NGX_LISTEN_BACKLOG;
为监听套接字的
backlog
参数赋初始值,控制内核维护的未完成连接队列 和已完成连接队列的总长度NGX_LISTEN_BACKLOG=511
lsopt.rcvbuf = -1;
将接收缓冲区大小(
SO_RCVBUF
)初始化为 -1 ,表示用户未在listen
指令中显式设置该参数
lsopt.sndbuf = -1;
将套接字的发送缓冲区大小(
SO_SNDBUF
)初始化为 -1 ,表示用户未在listen
指令中显式设置该参数
lsopt.fastopen = -1;
将
fastopen
字段初始化为 -1 ,表示用户未在listen
指令中显式设置该参数在 Nginx 源码中,
fastopen
字段用于控制 **TCP Fast Open(TFO)**功能的启用和队列长度设置,其意义和逻辑如下:
启用 TCP Fast Open
允许在 TCP 三次握手期间携带数据(减少 1 个 RTT 的延迟),提升短连接场景(如 HTTP 请求)的性能。
控制 TFO 队列长度
设置内核为 TFO 连接维护的队列长度(即同时处理的 TFO 连接数),防止 SYN 队列过载。
lsopt.ipv6only = 1;
设置监听套接字的
IPV6_V6ONLY
选项,决定是否允许 IPv4 映射地址(IPv4-mapped IPv6 addresses)通过 IPv6 套接字接收连接
for (n = 2; n < cf->args->nelts; n++) {
处理 listen 指令的其他参数
此时 cf->args->nelts=2, listen 指令没有其他参数
for (n = 0; n < u.naddrs; n++) {
for (i = 0; i < n; i++) {
if (ngx_cmp_sockaddr(u.addrs[n].sockaddr, u.addrs[n].socklen,
u.addrs[i].sockaddr, u.addrs[i].socklen, 1)
== NGX_OK)
{
goto next;
}
}
遍历解析后的地址列表
检查地址是否重复
此时 u.naddrs=1
lsopt.sockaddr = u.addrs[n].sockaddr;
lsopt.socklen = u.addrs[n].socklen;
lsopt.addr_text = u.addrs[n].name;
lsopt.wildcard = ngx_inet_wildcard(lsopt.sockaddr);
将通过
ngx_parse_url()
解析得到的网络地址信息(如sockaddr
、地址文本)存储到ngx_http_listen_opt_t
结构体,供后续套接字绑定和日志记录使用。
lsopt.wildcard = ngx_inet_wildcard(lsopt.sockaddr);
检查地址是否为通配符(0.0.0.0
或::
)此时 lsopt.wildcard=1
if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {
return NGX_CONF_ERROR;
}
ngx_http_add_listen-CSDN博客
return NGX_CONF_OK;