初始化監聽埠
微信公眾號:關注可瞭解更多的 Nginx
知識。任何問題或建議,請公眾號留言;
關注公眾號,有趣有內涵的文章第一時間送達!
初始化監聽埠
前言
上文介紹了 ngx_http_optimize_servers()
函式的一部分內容,下面繼續介紹剩下的重頭戲。
初始化埠
1for (p = 0; p < ports->nelts; p++) { 2// 前面的內容已經介紹完了 3if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) { 4return 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{ 4ngx_uint_ti, last, bind_wildcard; 5ngx_listening_t*ls; 6ngx_http_port_t*hport; 7ngx_http_conf_addr_t*addr; 8 9addr = port->addrs.elts; 10last = 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// 就是監聽最前面的元素的埠地址和最後一個元素的埠。 21if (addr[last - 1].opt.wildcard) { 22addr[last - 1].opt.bind = 1; 23bind_wildcard = 1; 24 25} else { 26bind_wildcard = 0; 27} 28 29i = 0; 30 31while (i < last) { 32 33if (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,參考上面的程式碼 37i++; 38continue; 39} 40 // 如果能執行到這裡,那麼有兩個條件 41// ① bind_wildcard=0,即不存在類似 listen *:80 的這種wildcard情況 42// ② bind_wildcard = 1, 即存在wildcard,但是當前的listen指令顯式的指定了bind屬性 43ls = ngx_http_add_listening(cf, &addr[i]); 44 45hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t)); 46 47ls->servers = hport; 48 49if (i == last - 1) { 50hport->naddrs = last; 51 52} else { 53hport->naddrs = 1; 54i = 0; 55} 56 57switch (ls->sockaddr->sa_family) { 58default: /* AF_INET */ 59if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) { 60return NGX_ERROR; 61} 62break; 63} 64 65addr++; 66last--; 67} 68 69return 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{ 4ngx_listening_t*ls; 5ngx_http_core_loc_conf_t*clcf; 6ngx_http_core_srv_conf_t*cscf; 7// 建立一個新的ngx_listening_t結構體,表示監聽的埠 8ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen); 9 10ls->addr_ntop = 1; 11//下面的一行程式碼很重要。在accept成功之後會呼叫ls->handler函式 12ls->handler = ngx_http_init_connection; 13 14cscf = addr->default_server; 15ls->pool_size = cscf->connection_pool_size; 16ls->post_accept_timeout = cscf->client_header_timeout; 17 18clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; 19 20ls->logp = clcf->error_log; 21ls->log.data = &ls->addr_text; 22ls->log.handler = ngx_accept_log_error; 23 24ls->backlog = addr->opt.backlog; 25ls->rcvbuf = addr->opt.rcvbuf; 26ls->sndbuf = addr->opt.sndbuf; 27 28#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) 29ls->accept_filter = addr->opt.accept_filter; 30#endif 31 32#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) 33ls->deferred_accept = addr->opt.deferred_accept; 34#endif 35 36#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) 37ls->ipv6only = addr->opt.ipv6only; 38#endif 39 40#if (NGX_HAVE_SETFIB) 41ls->setfib = addr->opt.setfib; 42#endif 43 44return ls; 45} 複製程式碼
上面的函式又呼叫了 ngx_create_listening()
,原始碼如下:
1ngx_listening_t * 2ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen) 3{ 4size_tlen; 5ngx_listening_t*ls; 6struct sockaddr*sa; 7u_chartext[NGX_SOCKADDR_STRLEN]; 8 // cycle->listening 陣列中儲存了監聽的埠的資訊 9ls = ngx_array_push(&cf->cycle->listening); 10 11ngx_memzero(ls, sizeof(ngx_listening_t)); 12// ngx_listening_t結構的sockaddr表示的是監聽的埠的結構 13sa = ngx_palloc(cf->pool, socklen); 14ngx_memcpy(sa, sockaddr, socklen); 15ls->sockaddr = sa; 16ls->socklen = socklen; 17 18//ngx_listening_t結構的addr_text就是監聽的地址資訊的字串,包括埠 19len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1); 20ls->addr_text.len = len; 21 22switch (ls->sockaddr->sa_family) { 23 24case AF_INET: 25ls->addr_text_max_len = NGX_INET_ADDRSTRLEN; 26break; 27} 28// ngx_listening_t結構的addr_text就是監聽的地址資訊的字串,包括埠 29ls->addr_text.data = ngx_pnalloc(cf->pool, len); 30ngx_memcpy(ls->addr_text.data, text, len); 31 32ls->fd = (ngx_socket_t) -1; 33ls->type = SOCK_STREAM;// 表示監聽的TCP 34 35ls->backlog = NGX_LISTEN_BACKLOG; 36ls->rcvbuf = -1; 37ls->sndbuf = -1; 38 39#if (NGX_HAVE_SETFIB) 40ls->setfib = -1; 41#endif 42 43return 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{ 12u_char*p; 13struct sockaddr_in*sin; 14 15switch (sa->sa_family) { 16 17case AF_INET: 18 19sin = (struct sockaddr_in *) sa; 20p = (u_char *) &sin->sin_addr; 21 22if (port) { 23p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d", 24p[0], p[1], p[2], p[3], ntohs(sin->sin_port)); 25} else { 26p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", 27p[0], p[1], p[2], p[3]); 28} 29 30return (p - text); 31} 32} 複製程式碼
資料結構
本文牽涉到的資料結構比較多,整理如下:
1typedef struct { 2/* ngx_http_in_addr_t or ngx_http_in6_addr_t */ 3void*addrs;// ngx_http_in_addr_t 陣列 4ngx_uint_tnaddrs; // 表示 addrs 陣列元素的個數 5} ngx_http_port_t; 6 7 8typedef struct { 9in_addr_taddr; // 當前監聽的地址 10ngx_http_addr_conf_tconf; //參考下面的資料結構 11} ngx_http_in_addr_t; 12 13 14typedef struct { 15/* the default server configuration for this address:port */ 16ngx_http_core_srv_conf_t*default_server; // 當前address:port的default server 17ngx_http_virtual_names_t*virtual_names; // 參考下面的資料結構 18 19#if (NGX_HTTP_SSL) 20ngx_uint_tssl;/* unsignedssl: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結構體 32ngx_hash_combined_tnames; 33 34ngx_uint_tnregex; 35ngx_http_server_name_t*regex; 36} ngx_http_virtual_names_t; 37 38 39typedef struct { 40ngx_hash_thash; 41ngx_hash_wildcard_t*wc_head; 42ngx_hash_wildcard_t*wc_tail; 43} ngx_hash_combined_t; 複製程式碼
喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達
