内容简介:微信公众号:关注可了解更多的关注公众号,有趣有内涵的文章第一时间送达!上文介绍了
微信公众号:关注可了解更多的 Nginx
知识。任何问题或建议,请公众号留言;
关注公众号,有趣有内涵的文章第一时间送达!
初始化监听端口
前言
上文介绍了 ngx_http_optimize_servers()
函数的一部分内容,下面继续介绍剩下的重头戏。
初始化端口
1for (p = 0; p < ports->nelts; p++) { 2// 前面的内容已经介绍完了 3 if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) { 4 return NGX_ERROR; 5 } 6} 复制代码
源码分析
下面我们详细介绍 ngx_http_init_listening()
函数,源码如下:
1static ngx_int_t 2ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port) 3{ 4 ngx_uint_t i, last, bind_wildcard; 5 ngx_listening_t *ls; 6 ngx_http_port_t *hport; 7 ngx_http_conf_addr_t *addr; 8 9 addr = port->addrs.elts; 10 last = port->addrs.nelts; 11 12 /* 13 * If there is a binding to an "*:port" then we need to bind() to 14 * the "*:port" only and ignore other implicit bindings. The bindings 15 * have been already sorted: explicit bindings are on the start, then 16 * implicit bindings go, and wildcard binding is in the end. 17 */ 18 // addr 是排过序的,放在最前面的是需要bind的 19 // addr 数组最后一个元素是宽绑定。即:*:port 20 // 就是监听最前面的元素的端口地址和最后一个元素的端口。 21 if (addr[last - 1].opt.wildcard) { 22 addr[last - 1].opt.bind = 1; 23 bind_wildcard = 1; 24 25 } else { 26 bind_wildcard = 0; 27 } 28 29 i = 0; 30 31 while (i < last) { 32 33 if (bind_wildcard && !addr[i].opt.bind) { 34 // 仔细分析一下,i的值就是那些没有显式的指定bind,将要被包含在wildcard中addr的数量 35// 因为 排序 之后的bind在最前面,所以当出现addr[i].opt.bind=0开始,那么后面的addr.opt.bind都为0, 36// 因为这是排序之后的数组。最后的一个元素的addr.opt.bind被nginx设置为了1,参考上面的代码 37 i++; 38 continue; 39 } 40 // 如果能执行到这里,那么有两个条件 41// ① bind_wildcard=0,即不存在类似 listen *:80 的这种wildcard情况 42// ② bind_wildcard = 1, 即存在wildcard,但是当前的listen指令显式的指定了bind属性 43 ls = ngx_http_add_listening(cf, &addr[i]); 44 45 hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t)); 46 47 ls->servers = hport; 48 49 if (i == last - 1) { 50 hport->naddrs = last; 51 52 } else { 53 hport->naddrs = 1; 54 i = 0; 55 } 56 57 switch (ls->sockaddr->sa_family) { 58 default: /* AF_INET */ 59 if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) { 60 return NGX_ERROR; 61 } 62 break; 63 } 64 65 addr++; 66 last--; 67 } 68 69 return NGX_OK; 70} 复制代码
监听端口
上面的源码中有一个非常重要的函数 ngx_http_add_listening()
,这个函数实现了对端口的监听。
1static ngx_listening_t * 2ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) 3{ 4 ngx_listening_t *ls; 5 ngx_http_core_loc_conf_t *clcf; 6 ngx_http_core_srv_conf_t *cscf; 7// 创建一个新的ngx_listening_t结构体,表示监听的端口 8 ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen); 9 10 ls->addr_ntop = 1; 11//下面的一行代码很重要。在accept成功之后会调用ls->handler函数 12 ls->handler = ngx_http_init_connection; 13 14 cscf = addr->default_server; 15 ls->pool_size = cscf->connection_pool_size; 16 ls->post_accept_timeout = cscf->client_header_timeout; 17 18 clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; 19 20 ls->logp = clcf->error_log; 21 ls->log.data = &ls->addr_text; 22 ls->log.handler = ngx_accept_log_error; 23 24 ls->backlog = addr->opt.backlog; 25 ls->rcvbuf = addr->opt.rcvbuf; 26 ls->sndbuf = addr->opt.sndbuf; 27 28#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) 29 ls->accept_filter = addr->opt.accept_filter; 30#endif 31 32#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) 33 ls->deferred_accept = addr->opt.deferred_accept; 34#endif 35 36#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) 37 ls->ipv6only = addr->opt.ipv6only; 38#endif 39 40#if (NGX_HAVE_SETFIB) 41 ls->setfib = addr->opt.setfib; 42#endif 43 44 return ls; 45} 复制代码
上面的函数又调用了 ngx_create_listening()
,源码如下:
1ngx_listening_t * 2ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen) 3{ 4 size_t len; 5 ngx_listening_t *ls; 6 struct sockaddr *sa; 7 u_char text[NGX_SOCKADDR_STRLEN]; 8 // cycle->listening 数组中保存了监听的端口的信息 9 ls = ngx_array_push(&cf->cycle->listening); 10 11 ngx_memzero(ls, sizeof(ngx_listening_t)); 12// ngx_listening_t结构的sockaddr表示的是监听的端口的结构 13 sa = ngx_palloc(cf->pool, socklen); 14 ngx_memcpy(sa, sockaddr, socklen); 15 ls->sockaddr = sa; 16 ls->socklen = socklen; 17 18//ngx_listening_t结构的addr_text就是监听的地址信息的字符串,包括端口 19 len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1); 20 ls->addr_text.len = len; 21 22 switch (ls->sockaddr->sa_family) { 23 24 case AF_INET: 25 ls->addr_text_max_len = NGX_INET_ADDRSTRLEN; 26 break; 27 } 28// ngx_listening_t结构的addr_text就是监听的地址信息的字符串,包括端口 29 ls->addr_text.data = ngx_pnalloc(cf->pool, len); 30 ngx_memcpy(ls->addr_text.data, text, len); 31 32 ls->fd = (ngx_socket_t) -1; 33 ls->type = SOCK_STREAM;// 表示监听的TCP 34 35 ls->backlog = NGX_LISTEN_BACKLOG; 36 ls->rcvbuf = -1; 37 ls->sndbuf = -1; 38 39#if (NGX_HAVE_SETFIB) 40 ls->setfib = -1; 41#endif 42 43 return ls; 44} 复制代码
上面还牵涉到一个比较简单的函数 ngx_sock_ntop()
,这个函数的功能很简单,就是把sockaddr格式的ip地址转换为一个字符串
1/* 2 *参数的含义: 3 * sa : 要转换为字符串的ip地址 4 * text: 保存转换之后的字符串 5 * len: sa参数的长度 6 * port: 0或者1,表示是否将端口也转换为字符串,如果为0则不转换,若为1则转换 7 * 返回值:转换的字符串的长度 8*/ 9size_t 10ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, ngx_uint_t port) 11{ 12 u_char *p; 13 struct sockaddr_in *sin; 14 15 switch (sa->sa_family) { 16 17 case AF_INET: 18 19 sin = (struct sockaddr_in *) sa; 20 p = (u_char *) &sin->sin_addr; 21 22 if (port) { 23 p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d", 24 p[0], p[1], p[2], p[3], ntohs(sin->sin_port)); 25 } else { 26 p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", 27 p[0], p[1], p[2], p[3]); 28 } 29 30 return (p - text); 31 } 32} 复制代码
数据结构
本文牵涉到的数据结构比较多,整理如下:
1typedef struct { 2 /* ngx_http_in_addr_t or ngx_http_in6_addr_t */ 3 void *addrs; // ngx_http_in_addr_t 数组 4 ngx_uint_t naddrs; // 表示 addrs 数组元素的个数 5} ngx_http_port_t; 6 7 8typedef struct { 9 in_addr_t addr; // 当前监听的地址 10 ngx_http_addr_conf_t conf; //参考下面的数据结构 11} ngx_http_in_addr_t; 12 13 14typedef struct { 15 /* the default server configuration for this address:port */ 16 ngx_http_core_srv_conf_t *default_server; // 当前address:port的default server 17 ngx_http_virtual_names_t *virtual_names; // 参考下面的数据结构 18 19#if (NGX_HTTP_SSL) 20 ngx_uint_t ssl; /* unsigned ssl:1; */ // 表示listen指令是否使用了ssl 21#endif 22} ngx_http_addr_conf_t; 23 24 25typedef struct { 26// 参考下面的数据结构 27// name的hash指向address:port的hash 28// name的wc_head指向address:port的wc_head 29// name的wc_tail指向address:port的wc_tail 30// 也就是说把 address:port 的 hash,wc_head,wc_tail组合成一个ngx_hash_combined_t类型的数据结构,为后面做准备. 31// 因为后面使用到的都是 ngx_http_port_t 结构体,不再使用address:port对应的ngx_http_conf_addr_t结构体 32 ngx_hash_combined_t names; 33 34 ngx_uint_t nregex; 35 ngx_http_server_name_t *regex; 36} ngx_http_virtual_names_t; 37 38 39typedef struct { 40 ngx_hash_t hash; 41 ngx_hash_wildcard_t *wc_head; 42 ngx_hash_wildcard_t *wc_tail; 43} ngx_hash_combined_t; 复制代码
喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Laravel 给生产环境添加监听事件 - SQL日志监听
- C++ 的一大误区——深入解释直接初始化与复制初始化的区别
- Flutter事件监听
- Vue简便监听事件
- react源码-事件监听
- Oracle监听日志定期清理
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JSON 在线解析
在线 JSON 格式化工具
Markdown 在线编辑器
Markdown 在线编辑器