Публичный аккаунт WeChat:Чжэн Эрдос
Подпишитесь, чтобы узнать больше о Nginx. Любые вопросы или предложения, пожалуйста, оставьте сообщение в публичном аккаунте;
Обратите внимание на официальный аккаунт, интересные и содержательные статьи будут доставлены в ближайшее время!
обзор сюжета
В предыдущей статье мы проанализировали процесс синтаксического анализа директивы location и кратко рассмотрели содержимое: каждое расположение соответствует структуре ngx_http_core_loc_conf_t, и все расположения связаны друг с другом через двустороннюю очередь. Структура данных более сложная.
слушай директиву
Начиная с этой статьи, мы разбираем процесс парсинга команды 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
Разобрать uri и порт в директиве listen
Как видно из вышеизложенного, у listen есть множество применений.Нам нужно получить номер порта и часть uri команды listen при синтаксическом анализе.nginx предоставляетngx_parse_url()
для анализа uri и порта, эта функция будет вызываться при разборе команды 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}
Эта функция анализирует адрес и номер порта нашего прослушивания, в нашем конфигурационном файле номер порта равен 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}
Из файла конфигурации мы можем узнать, что прослушивание может отображаться только в модуле сервера и может иметь несколько параметров.
Соответствующая функция обработки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
, как следует из названия, эта структура предназначена для сохранения некоторых параметров порта прослушивания (параметр порта прослушивания).
Вот функция, называемаяngx_parse_url()
, мы уже разобрали выше, функция этой функции заключается в разборе адреса и порта в url.
Затем наступает самая важная часть,ngx_http_core_listen()
Функция вызывается в концеngx_http_add_listen()
Функция, которая сохраняет информацию о порте прослушиванияngx_http_core_main_conf_t
В портах динамический массив структуры.
функция 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
в портовом поле конструкции.
Друзья, которым понравилась эта статья, пожалуйста, нажмите и удерживайте изображение, чтобы подписаться на номер подписки Чжэн Эрдос, более интересный контент будет доставлен как можно скорее.