初始化監聽埠

鄭爾多斯發表於2019-02-27

微信公眾號:鄭爾多斯
關注可瞭解更多的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 陣列元素的個數
5ngx_http_port_t;
6
7
8typedef struct {
9    in_addr_t                  addr; // 當前監聽的地址
10    ngx_http_addr_conf_t       conf; //參考下面的資料結構
11ngx_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
22ngx_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;
36ngx_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;
43ngx_hash_combined_t;
複製程式碼

喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達

鄭爾多斯
鄭爾多斯

相關文章