内容简介:微信公众号:关注可了解更多的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字段中。
喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Dockerfile指令解析
- Nginx Location 指令语法解析
- 全网最全 Dalvik 指令集解析 !
- Android逆向之旅—最右App的签名算法解析(ARM指令学习回首篇)
- redis 用scan指令 代替keys指令(详解)
- vue 基本指令(四)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。