nginx的listen指令解析

栏目: 服务器 · Nginx · 发布时间: 5年前

内容简介:微信公众号:关注可了解更多的Nginx知识。任何问题或建议,请公众号留言;关注公众号,有趣有内涵的文章第一时间送达!上一篇文章我们分析了location指令的解析过程,简单的回顾一下这个内容:每个location对应一个ngx_http_core_loc_conf_t结构体,所有的location通过一个双向队列连接在一起。数据结构比较复杂。

微信公众号:关注可了解更多的Nginx知识。任何问题或建议,请公众号留言;

关注公众号,有趣有内涵的文章第一时间送达!

剧情回顾

上一篇文章我们分析了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]];
复制代码

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

1listen 127.0.0.1:8000;
2listen 127.0.0.1 不加端口,默认监听80端口;
3listen 8000
4listen *:8000
5listen localhost:8000
复制代码

解析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}
复制代码

我们使用的是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        }
101
102        u->no_port = 1;
103        u->port = u->default_port;
104        sin->sin_port = htons(u->default_port);
105    }
106
107    len = last - host;
108
109    if (len == 0) {
110        u->err = "no host";
111        return NGX_ERROR;
112    }
113
114    u->host.len = len;
115    u->host.data = host;
116
117    if (u->listen && len == 1 && *host == '*') {
118        sin->sin_addr.s_addr = INADDR_ANY;
119        u->wildcard = 1;
120        return NGX_OK;
121    }
122
123    sin->sin_addr.s_addr = ngx_inet_addr(host, len);
124
125    if (sin->sin_addr.s_addr != INADDR_NONE) {
126
127        if (sin->sin_addr.s_addr == INADDR_ANY) {
128            u->wildcard = 1;
129        }
130
131        u->naddrs = 1;
132
133        u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
134        if (u->addrs == NULL) {
135            return NGX_ERROR;
136        }
137
138        sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
139        if (sin == NULL) {
140            return NGX_ERROR;
141        }
142
143        ngx_memcpy(sin, &u->sockaddr, sizeof(struct sockaddr_in));
144
145        u->addrs[0].sockaddr = (struct sockaddr *) sin;
146        u->addrs[0].socklen = sizeof(struct sockaddr_in);
147
148        p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);
149        if (p == NULL) {
150            return NGX_ERROR;
151        }
152
153        u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",
154                                           &u->host, u->port) - p;
155        u->addrs[0].name.data = p;
156
157        return NGX_OK;
158    }
159
160    if (u->no_resolve) {
161        return NGX_OK;
162    }
163
164    if (ngx_inet_resolve_host(pool, u) != NGX_OK) {
165        return NGX_ERROR;
166    }
167
168    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);
171
172    switch (u->family) {
173
174#if (NGX_HAVE_INET6)
175    case AF_INET6:
176        sin6 = (struct sockaddr_in6 *) &u->sockaddr;
177
178        if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
179            u->wildcard = 1;
180        }
181
182        break;
183#endif
184
185    default: /* AF_INET */
186        sin = (struct sockaddr_in *) &u->sockaddr;
187
188        if (sin->sin_addr.s_addr == INADDR_ANY) {
189            u->wildcard = 1;
190        }
191
192        break;
193    }
194
195    return NGX_OK;
196}
复制代码

这个函数就是解析了我们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}
复制代码

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

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

 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;
10
11    cscf->listen = 1;
12
13    value = cf->args->elts;
14
15    ngx_memzero(&u, sizeof(ngx_url_t));
16
17    u.url = value[1];
18    u.listen = 1;
19    u.default_port = 80;
20
21    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
22        return NGX_CONF_ERROR;
23    }
24
25    ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
26
27    ngx_memcpy(&lsopt.sockaddr.sockaddr, &u.sockaddr, u.socklen);
28
29    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#endif
36#if (NGX_HAVE_TCP_FASTOPEN)
37    lsopt.fastopen = -1;
38#endif
39    lsopt.wildcard = u.wildcard;
40#if (NGX_HAVE_INET6)
41    lsopt.ipv6only = 1;
42#endif
43
44    (void) ngx_sock_ntop(&lsopt.sockaddr.sockaddr, lsopt.socklen, lsopt.addr,
45                         NGX_SOCKADDR_STRLEN, 1);
46
47    for (n = 2; n < cf->args->nelts; n++) {
48
49        if (ngx_strcmp(value[n].data, "default_server") == 0
50            || ngx_strcmp(value[n].data, "default") == 0)
51        {
52            lsopt.default_server = 1;
53            continue;
54        }
55       // 这里面的其他代码都是处理listen的各种参数,对我们这里的分析没有用处
56    }
57
58    if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) {
59        return NGX_CONF_OK;
60    }
61
62    return NGX_CONF_ERROR;
63}
复制代码

这个函数的整体流程就是解析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_t
14    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    }
23
24    sa = &lsopt->sockaddr.sockaddr;
25    p = ngx_inet_get_port(sa);
26
27    port = cmcf->ports->elts;
28    for (i = 0; i < cmcf->ports->nelts; i++) {
29
30        if (p != port[i].port || sa->sa_family != port[i].family) {
31            continue;
32        }
33
34        /* a port is already in the port list */
35
36        return ngx_http_add_addresses(cf, cscf, &port[i], lsopt);
37    }
38
39    /* add a port to the port list */
40
41    port = ngx_array_push(cmcf->ports);
42    if (port == NULL) {
43        return NGX_ERROR;
44    }
45
46    port->family = sa->sa_family;
47    port->port = p;
48    port->addrs.elts = NULL;
49
50    return ngx_http_add_address(cf, cscf, port, lsopt);
51}
复制代码

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

nginx的listen指令解析
listen指令解析之后的格式

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

nginx的listen指令解析
郑尔多斯

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

MATLAB实用教程

MATLAB实用教程

穆尔(Holly Moore) / 高会生 刘童娜 李聪聪 / 电子工业出版社 / 2010-1-1 / 59.00元

MATLAB实用教程(第二版),ISBN:9787121101793,作者:(美)穆尔 著,高会生,刘童娜,李聪聪 译一起来看看 《MATLAB实用教程》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码