nginx中的listen指令怎么用

listen指令

nginx作為一個高性能的http服務器,網絡的處理是其核心,了解網絡的初始化有助于加深對nginx網絡處理的了解。與網絡有關的配置命令主要有兩個:listen和sever_name。listen命令設置nginx監聽地址,對于ip協議,這個地址就是address和port,對于unix域套接字協議,這個地址就是path,一條listen指令只能指定一個address或者port,address也可以是主機名

從這一篇文章開始,我們分析listen指令的解析過程,listen指令的配置如下:從nginx.org的手冊中我們可以獲取listen的使用方法:

listen?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指令攜帶的參數是很復雜的。不過,我們一般很少關注那些不太常用的參數,以下是一些常用的配置方式:

listen?127.0.0.1:8000; listen?127.0.0.1?不加端口,默認監聽80端口; listen?8000 listen?*:8000 listen?localhost:8000

解析listen指令中的uri和端口

從上面的內容知道,listen有多種用法,我們在解析的時候需要獲取到listen指令的端口號和uri部分,nginx提供了ngx_parse_url()方法來解析uri和port,該函數在解析listen指令的時候會被調用。

ngx_int_t ngx_parse_url(ngx_pool_t?*pool,?ngx_url_t?*u) { ?u_char?*p; ?size_t?len;  ?p?=?u->url.data; ?len?=?u->url.len; ?//?這里是解析unix?domain的協議 ?if?(len?>=?5?&&?ngx_strncasecmp(p,?(u_char?*)?"unix:",?5)?==?0)?{ ?return?ngx_parse_unix_domain_url(pool,?u); ?} ?//?解析ipv6協議 ?if?(len?&&?p[0]?==?'[')?{ ?return?ngx_parse_inet6_url(pool,?u); ?} ?//?解析ipv4協議 ?return?ngx_parse_inet_url(pool,?u); }

我們使用的是ipv4協議,這里分析ngx_parse_inet_url()函數

//?u.url?=?"80"; //?u.listen?=?1; //?u.default_port?=?80; static?ngx_int_t ngx_parse_inet_url(ngx_pool_t?*pool,?ngx_url_t?*u) { ?u_char?*p,?*host,?*port,?*last,?*uri,?*args; ?size_t?len; ?ngx_int_t?n; ?struct?sockaddr_in?*sin; #if?(ngx_have_inet6) ?struct?sockaddr_in6?*sin6; #endif  ?u->socklen?=?sizeof(struct?sockaddr_in); ?sin?=?(struct?sockaddr_in?*)?&u->sockaddr; ?sin->sin_family?=?af_inet;//?ipv4類型  ?u->family?=?af_inet;?  ?host?=?u->url.data;?//?"80"  ?last?=?host?+?u->url.len;?//?host的最后字符的位置  ?port?=?ngx_strlchr(host,?last,?':');?//?找到port,?這里為?null  ?uri?=?ngx_strlchr(host,?last,?'/');?//?找到uri,這里為?null  ?args?=?ngx_strlchr(host,?last,?'?');?//?找到參數args,這里為?null  ?if?(args)?{ ?if?(uri?==?null?||?args?listen?||?!u->uri_part)?{ ?u->err?=?"invalid?host"; ?return?ngx_error; ?}  ?u->uri.len?=?last?-?uri; ?u->uri.data?=?uri;  ?last?=?uri;  ?if?(uri??65535)?{ ?u->err?=?"invalid?port"; ?return?ngx_error; ?}  ?u->port?=?(in_port_t)?n; ?sin->sin_port?=?htons((in_port_t)?n);  ?u->port_text.len?=?len; ?u->port_text.data?=?port;  ?last?=?port?-?1;  ?}?else?{ ?if?(uri?==?null)?{  ?if?(u->listen)?{  ?/*?test?value?as?port?only?*/  ?n?=?ngx_atoi(host,?last?-?host);  ?if?(n?!=?ngx_error)?{  ?if?(n??65535)?{ ?u->err?=?"invalid?port"; ?return?ngx_error; ?}  ?u->port?=?(in_port_t)?n; ?sin->sin_port?=?htons((in_port_t)?n);  ?u->port_text.len?=?last?-?host; ?u->port_text.data?=?host;  ?u->wildcard?=?1;  ?return?ngx_ok; ?} ?} ?}  ?u->no_port?=?1; ?u->port?=?u->default_port; ?sin->sin_port?=?htons(u->default_port); ?}  ?len?=?last?-?host;  ?if?(len?==?0)?{ ?u->err?=?"no?host"; ?return?ngx_error; ?}  ?u->host.len?=?len; ?u->host.data?=?host;  ?if?(u->listen?&&?len?==?1?&&?*host?==?'*')?{ ?sin->sin_addr.s_addr?=?inaddr_any; ?u->wildcard?=?1; ?return?ngx_ok; ?}  ?sin->sin_addr.s_addr?=?ngx_inet_addr(host,?len);  ?if?(sin->sin_addr.s_addr?!=?inaddr_none)?{  ?if?(sin->sin_addr.s_addr?==?inaddr_any)?{ ?u->wildcard?=?1; ?}  ?u->naddrs?=?1;  ?u->addrs?=?ngx_pcalloc(pool,?sizeof(ngx_addr_t)); ?if?(u->addrs?==?null)?{ ?return?ngx_error; ?}  ?sin?=?ngx_pcalloc(pool,?sizeof(struct?sockaddr_in)); ?if?(sin?==?null)?{ ?return?ngx_error; ?}  ?ngx_memcpy(sin,?&u->sockaddr,?sizeof(struct?sockaddr_in));  ?u->addrs[0].sockaddr?=?(struct?sockaddr?*)?sin; ?u->addrs[0].socklen?=?sizeof(struct?sockaddr_in);  ?p?=?ngx_pnalloc(pool,?u->host.len?+?sizeof(":65535")?-?1); ?if?(p?==?null)?{ ?return?ngx_error; ?}  ?u->addrs[0].name.len?=?ngx_sprintf(p,?"%v:%d", ??&u->host,?u->port)?-?p; ?u->addrs[0].name.data?=?p;  ?return?ngx_ok; ?}  ?if?(u->no_resolve)?{ ?return?ngx_ok; ?}  ?if?(ngx_inet_resolve_host(pool,?u)?!=?ngx_ok)?{ ?return?ngx_error; ?}  ?u->family?=?u->addrs[0].sockaddr->sa_family; ?u->socklen?=?u->addrs[0].socklen; ?ngx_memcpy(&u->sockaddr,?u->addrs[0].sockaddr,?u->addrs[0].socklen);  ?switch?(u->family)?{  #if?(ngx_have_inet6) ?case?af_inet6: ?sin6?=?(struct?sockaddr_in6?*)?&u->sockaddr;  ?if?(in6_is_addr_unspecified(&sin6->sin6_addr))?{ ?u->wildcard?=?1; ?}  ?break; #endif  ?default:?/*?af_inet?*/ ?sin?=?(struct?sockaddr_in?*)?&u->sockaddr;  ?if?(sin->sin_addr.s_addr?==?inaddr_any)?{ ?u->wildcard?=?1; ?}  ?break; ?}  ?return?ngx_ok; }

這個函數就是解析了我們listen的地址和端口號,我們的配置文件中,端口號為80,并沒有配置監聽地址,所以u->wildcard = 1,表示這是一個通配符,要監聽該服務器所有ip地址的這個端口號。

解析listen指令

下面從源碼中看一下listen的配置:

{? ?ngx_string("listen"), ?ngx_http_srv_conf|ngx_conf_1more, ?ngx_http_core_listen, ?ngx_http_srv_conf_offset, ?0, ?null? }

從配置文件中我們可以知道,listen只能出現在server 模塊中,可以帶有多個參數。

對應的處理函數為 ngx_http_core_listen,下面我們分析這個函數,我們刪除了一些進行錯誤判斷的代碼,

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; ?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)?{ ?return?ngx_conf_error; ?}  ?ngx_memzero(&lsopt,?sizeof(ngx_http_listen_opt_t));  ?ngx_memcpy(&lsopt.sockaddr.sockaddr,?&u.sockaddr,?u.socklen);  ?lsopt.socklen?=?u.socklen; ?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 ?lsopt.wildcard?=?u.wildcard; #if?(ngx_have_inet6) ?lsopt.ipv6only?=?1; #endif  ?(void)?ngx_sock_ntop(&lsopt.sockaddr.sockaddr,?lsopt.socklen,?lsopt.addr, ??ngx_sockaddr_strlen,?1);  ?for?(n?=?2;?n?args->nelts;?n++)?{  ?if?(ngx_strcmp(value[n].data,?"default_server")?==?0 ?||?ngx_strcmp(value[n].data,?"default")?==?0) ?{ ?lsopt.default_server?=?1; ?continue; ?} ?//?這里面的其他代碼都是處理listen的各種參數,對我們這里的分析沒有用處 ?}  ?if?(ngx_http_add_listen(cf,?cscf,?&lsopt)?==?ngx_ok)?{ ?return?ngx_conf_ok; ?}  ?return?ngx_conf_error; }

這個函數的整體流程就是解析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()函數

//?cf:?配置結構體 //?cscf:?listen指令所在的server的配置結構體 //?lsopt?:?ngx_http_core_listen()生成的listen?option ngx_int_t ngx_http_add_listen(ngx_conf_t?*cf,?ngx_http_core_srv_conf_t?*cscf, ?ngx_http_listen_opt_t?*lsopt) { ?in_port_t?????p; ?ngx_uint_t?????i; ?struct?sockaddr???*sa; ?ngx_http_conf_port_t??*port; ?ngx_http_core_main_conf_t?*cmcf; ?//?獲取?ngx_http_core_module模塊的main_conf結構體ngx_http_core_main_conf_t ?cmcf?=?ngx_http_conf_get_module_main_conf(cf,?ngx_http_core_module); ?//?ports字段是一個數組 ?if?(cmcf->ports?==?null)?{ ??cmcf->ports?=?ngx_array_create(cf->temp_pool,?2, ??????????sizeof(ngx_http_conf_port_t)); ??if?(cmcf->ports?==?null)?{ ???return?ngx_error; ??} ?}  ?sa?=?&lsopt->sockaddr.sockaddr; ?p?=?ngx_inet_get_port(sa);  ?port?=?cmcf->ports->elts; ?for?(i?=?0;?i?ports->nelts;?i++)?{  ??if?(p?!=?port[i].port?||?sa->sa_family?!=?port[i].family)?{ ???continue; ??}  ??/*?a?port?is?already?in?the?port?list?*/  ??return?ngx_http_add_addresses(cf,?cscf,?&port[i],?lsopt); ?}  ?/*?add?a?port?to?the?port?list?*/  ?port?=?ngx_array_push(cmcf->ports); ?if?(port?==?null)?{ ??return?ngx_error; ?}  ?port->family?=?sa->sa_family; ?port->port?=?p; ?port->addrs.elts?=?null;  ?return?ngx_http_add_address(cf,?cscf,?port,?lsopt); }

這個函數將端口號的信息保存到了 ngx_http_core_main_conf_t結構體的port字段中。

nginx中的listen指令怎么用

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享