1. 首页

nginx的listen指令解析你懂了吗

剧情回顾

上一篇文章我们分析了location指令的解析过程,简单的回顾一下这个内容:每个location对应一个ngx_http_core_loc_conf_t结构体,所有的location通过一个双向队列连接在一起。数据结构比较复杂。

listen指令

从这一篇文章开始,我们分析listen指令的解析过程,listen指令的配置如下:
从nginx.org的手册中我们可以获取listen的使用方法:


1listen address[:port] [default_server] [setfib=number] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [ipv6only=on|off] [ssl] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]]; //JS中文网 – 前端进阶资源分享 https://www.javascriptc.com/ 趣聊CSS系列

一个listen指令携带的参数是很复杂的。不过,我们一般很少关注那些不太常用的参数,以下是一些常用的配置方式:


1listen 127.0.0.1:8000;2listen 127.0.0.1 不加端口,默认监听80端口;3listen 80004listen *:80005listen localhost:8000 //JS中文网 – 前端进阶资源分享 https://www.javascriptc.com/ 趣聊CSS系列

解析listen指令中的uri和端口

从上面的内容知道,listen有多种用法,我们在解析的时候需要获取到listen指令的端口号和uri部分,nginx提供了ngx_parse_url()方法来解析uri和port,该函数在解析listen指令的时候会被调用。


1ngx_int_t 2ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u) 3{ 4    u_char  *p; 5    size_t   len; 6 7    p = u->url.data; 8    len = u->url.len; 9    // 这里是解析unix domain的协议10    if (len >= 5 && ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) {11        return ngx_parse_unix_domain_url(pool, u);12    }13   // 解析IPV6协议14    if (len && p[0] == '[') {15        return ngx_parse_inet6_url(pool, u);16    }17   // 解析IPV4协议18    return ngx_parse_inet_url(pool, u);19} //JS中文网 – 前端进阶资源分享 https://www.javascriptc.com/ 趣聊CSS系列

我们使用的是IPV4协议,这里分析ngx_parse_inet_url()函数


1//    u.url = "80"; 2//    u.listen = 1; 3//    u.default_port = 80; 4static ngx_int_t 5ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) 6{ 7    u_char               *p, *host, *port, *last, *uri, *args; 8    size_t                len; 9    ngx_int_t             n; 10    struct sockaddr_in   *sin; 11#if (NGX_HAVE_INET6) 12    struct sockaddr_in6  *sin6; 13#endif 14 15    u->socklen = sizeof(struct sockaddr_in); 16    sin = (struct sockaddr_in *) &u->sockaddr; 17    sin->sin_family = AF_INET;// IPV4类型 18 19    u->family = AF_INET;  20 21    host = u->url.data; // "80" 22 23    last = host + u->url.len; // host的最后字符的位置 24 25    port = ngx_strlchr(host, last, ':'); // 找到port, 这里为 NULL 26 27    uri = ngx_strlchr(host, last, '/'); // 找到uri,这里为 NULL 28 29    args = ngx_strlchr(host, last, '?'); // 找到参数args,这里为 NULL 30 31    if (args) { 32        if (uri == NULL || args < uri) { 33            uri = args; 34        } 35    } 36 37    if (uri) { 38        if (u->listen || !u->uri_part) { 39            u->err = "invalid host"; 40            return NGX_ERROR; 41        } 42 43        u->uri.len = last - uri; 44        u->uri.data = uri; 45 46        last = uri; 47 48        if (uri < port) { 49            port = NULL; 50        } 51    } 52 53    if (port) { 54        port++; 55 56        len = last - port; 57 58        n = ngx_atoi(port, len); 59 60        if (n < 1 || n > 65535) { 61            u->err = "invalid port"; 62            return NGX_ERROR; 63        } 64 65        u->port = (in_port_t) n; 66        sin->sin_port = htons((in_port_t) n); 67 68        u->port_text.len = len; 69        u->port_text.data = port; 70 71        last = port - 1; 72 73    } else { 74        if (uri == NULL) { 75 76            if (u->listen) { 77 78                /* test value as port only */ 79 80                n = ngx_atoi(host, last - host); 81 82                if (n != NGX_ERROR) { 83 84                    if (n < 1 || n > 65535) { 85                        u->err = "invalid port"; 86                        return NGX_ERROR; 87                    } 88 89                    u->port = (in_port_t) n; 90                    sin->sin_port = htons((in_port_t) n); 91 92                    u->port_text.len = last - host; 93                    u->port_text.data = host; 94 95                    u->wildcard = 1; 96 97                    return NGX_OK; 98                } 99            }100        }101102        u->no_port = 1;103        u->port = u->default_port;104        sin->sin_port = htons(u->default_port);105    }106107    len = last - host;108109    if (len == 0) {110        u->err = "no host";111        return NGX_ERROR;112    }113114    u->host.len = len;115    u->host.data = host;116117    if (u->listen && len == 1 && *host == '*') {118        sin->sin_addr.s_addr = INADDR_ANY;119        u->wildcard = 1;120        return NGX_OK;121    }122123    sin->sin_addr.s_addr = ngx_inet_addr(host, len);124125    if (sin->sin_addr.s_addr != INADDR_NONE) {126127        if (sin->sin_addr.s_addr == INADDR_ANY) {128            u->wildcard = 1;129        }130131        u->naddrs = 1;132133        u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));134        if (u->addrs == NULL) {135            return NGX_ERROR;136        }137138        sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));139        if (sin == NULL) {140            return NGX_ERROR;141        }142143        ngx_memcpy(sin, &u->sockaddr, sizeof(struct sockaddr_in));144145        u->addrs[0].sockaddr = (struct sockaddr *) sin;146        u->addrs[0].socklen = sizeof(struct sockaddr_in);147148        p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);149        if (p == NULL) {150            return NGX_ERROR;151        }152153        u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",154                                           &u->host, u->port) - p;155        u->addrs[0].name.data = p;156157        return NGX_OK;158    }159160    if (u->no_resolve) {161        return NGX_OK;162    }163164    if (ngx_inet_resolve_host(pool, u) != NGX_OK) {165        return NGX_ERROR;166    }167168    u->family = u->addrs[0].sockaddr->sa_family;169    u->socklen = u->addrs[0].socklen;170    ngx_memcpy(&u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen);171172    switch (u->family) {173174#if (NGX_HAVE_INET6)175    case AF_INET6:176        sin6 = (struct sockaddr_in6 *) &u->sockaddr;177178        if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {179            u->wildcard = 1;180        }181182        break;183#endif184185    default: /* AF_INET */186        sin = (struct sockaddr_in *) &u->sockaddr;187188        if (sin->sin_addr.s_addr == INADDR_ANY) {189            u->wildcard = 1;190        }191192        break;193    }194195    return NGX_OK;196} //JS中文网 – 前端进阶资源分享 https://www.javascriptc.com/ 趣聊CSS系列

这个函数就是解析了我们listen的地址和端口号,我们的配置文件中,端口号为80,并没有配置监听地址,所以u->wildcard = 1,表示这是一个通配符,要监听该服务器所有ip地址的这个端口号。

解析listen指令

下面从源码中看一下listen的配置:


1{ 2      ngx_string("listen"),3      NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,4      ngx_http_core_listen,5      NGX_HTTP_SRV_CONF_OFFSET,6      0,7      NULL 8} //JS中文网 – 前端进阶资源分享 https://www.javascriptc.com/ 趣聊CSS系列

从配置文件中我们可以知道,listen只能出现在server 模块中,可以带有多个参数。

对应的处理函数为 ngx_http_core_listen,下面我们分析这个函数,我们删除了一些进行错误判断的代码,

 c
 1static char * 2ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3{ 4    ngx_http_core_srv_conf_t *cscf = conf; 5 6    ngx_str_t              *value, size; 7    ngx_url_t               u; 8    ngx_uint_t              n; 9    ngx_http_listen_opt_t   lsopt;1011    cscf->listen = 1;1213    value = cf->args->elts;1415    ngx_memzero(&u, sizeof(ngx_url_t));1617    u.url = value[1];18    u.listen = 1;19    u.default_port = 80;2021    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {22        return NGX_CONF_ERROR;23    }2425    ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));2627    ngx_memcpy(&lsopt.sockaddr.sockaddr, &u.sockaddr, u.socklen);2829    lsopt.socklen = u.socklen;30    lsopt.backlog = NGX_LISTEN_BACKLOG;31    lsopt.rcvbuf = -1;32    lsopt.sndbuf = -1;33#if (NGX_HAVE_SETFIB)34    lsopt.setfib = -1;35#endif36#if (NGX_HAVE_TCP_FASTOPEN)37    lsopt.fastopen = -1;38#endif39    lsopt.wildcard = u.wildcard;40#if (NGX_HAVE_INET6)41    lsopt.ipv6only = 1;42#endif4344    (void) ngx_sock_ntop(&lsopt.sockaddr.sockaddr, lsopt.socklen, lsopt.addr,45                         NGX_SOCKADDR_STRLEN, 1);4647    for (n = 2; n < cf->args->nelts; n++) {4849        if (ngx_strcmp(value[n].data, "default_server") == 050            || ngx_strcmp(value[n].data, "default") == 0)51        {52            lsopt.default_server = 1;53            continue;54        }55       // 这里面的其他代码都是处理listen的各种参数,对我们这里的分析没有用处56    }5758    if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) {59        return NGX_CONF_OK;60    }6162    return NGX_CONF_ERROR;63}
//JS中文网 – 前端进阶资源分享 https://www.javascriptc.com/ 趣聊CSS系列

这个函数的整体流程就是解析listen指令的各个参数,生成一个 ngx_http_listen_opt_t,顾名思义,这个结构体就是保存一些监听端口的选项(listening port option)。
这里调用了一个函数ngx_parse_url(),我们上面已经分析过了,这个函数的作用就是解析url中的address和port。

然后最重要的部分就要到了,ngx_http_core_listen()函数在最后面调用了ngx_http_add_listen()函数,该函数是将listen的端口信息保存到ngx_http_core_main_conf_t结构体的ports动态数组中。

ngx_http_add_listen()函数


1// cf: 配置结构体 2// cscf: listen指令所在的server的配置结构体 3// lsopt : ngx_http_core_listen()生成的listen option 4ngx_int_t 5ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, 6    ngx_http_listen_opt_t *lsopt) 7{ 8    in_port_t                   p; 9    ngx_uint_t                  i;10    struct sockaddr            *sa;11    ngx_http_conf_port_t       *port;12    ngx_http_core_main_conf_t  *cmcf;13   // 获取 ngx_http_core_module模块的main_conf结构体ngx_http_core_main_conf_t14    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);15   // ports字段是一个数组16    if (cmcf->ports == NULL) {17        cmcf->ports = ngx_array_create(cf->temp_pool, 2,18                                       sizeof(ngx_http_conf_port_t));19        if (cmcf->ports == NULL) {20            return NGX_ERROR;21        }22    }2324    sa = &lsopt->sockaddr.sockaddr;25    p = ngx_inet_get_port(sa);2627    port = cmcf->ports->elts;28    for (i = 0; i < cmcf->ports->nelts; i++) {2930        if (p != port[i].port || sa->sa_family != port[i].family) {31            continue;32        }3334        /* a port is already in the port list */3536        return ngx_http_add_addresses(cf, cscf, &port[i], lsopt);37    }3839    /* add a port to the port list */4041    port = ngx_array_push(cmcf->ports);42    if (port == NULL) {43        return NGX_ERROR;44    }4546    port->family = sa->sa_family;47    port->port = p;48    port->addrs.elts = NULL;4950    return ngx_http_add_address(cf, cscf, port, lsopt);51} //JS中文网 – 前端进阶资源分享 https://www.javascriptc.com/ 趣聊CSS系列

这个函数将端口号的信息保存到了 ngx_http_core_main_conf_t结构体的port字段中。

listen指令解析之后的格式

listen指令解析之后的格式


喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达

郑尔多斯

郑尔多斯

作者:郑尔多斯
链接:https://juejin.im/post/6844903729309859854

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:nginx的listen指令解析你懂了吗

链接:https://www.javascriptc.com/4219.html

« LeetCode 050. Pow(x, n)
es6 深入理解 Proxy 和 Reflect 这对影子兄弟»
Flutter 中文教程资源

相关推荐

QR code