初始化監(jiān)聽(tīng)端口

微信公眾號(hào):鄭爾多斯
關(guān)注可了解更多的Nginx知識(shí)。任何問(wèn)題或建議,請(qǐng)公眾號(hào)留言;
關(guān)注公眾號(hào),有趣有內(nèi)涵的文章第一時(shí)間送達(dá)!

初始化監(jiān)聽(tīng)端口

前言

上文介紹了ngx_http_optimize_servers()函數(shù)的一部分內(nèi)容,下面繼續(xù)介紹剩下的重頭戲。

初始化端口

for (p = 0; p < ports->nelts; p++) {
// 前面的內(nèi)容已經(jīng)介紹完了
        if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
            return NGX_ERROR;
        }
}

源碼分析

下面我們?cè)敿?xì)介紹ngx_http_init_listening()函數(shù),源碼如下:

static ngx_int_t
ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
{
    ngx_uint_t                 i, last, bind_wildcard;
    ngx_listening_t           *ls;
    ngx_http_port_t           *hport;
    ngx_http_conf_addr_t      *addr;

    addr = port->addrs.elts;
    last = port->addrs.nelts;

    /*
     * If there is a binding to an "*:port" then we need to bind() to
     * the "*:port" only and ignore other implicit bindings.  The bindings
     * have been already sorted: explicit bindings are on the start, then
     * implicit bindings go, and wildcard binding is in the end.
     */
 // addr 是排過(guò)序的,放在最前面的是需要bind的
    // addr 數(shù)組最后一個(gè)元素是寬綁定。即:*:port
    // 就是監(jiān)聽(tīng)最前面的元素的端口地址和最后一個(gè)元素的端口。
    if (addr[last - 1].opt.wildcard) {
        addr[last - 1].opt.bind = 1;
        bind_wildcard = 1;

    } else {
        bind_wildcard = 0;
    }

    i = 0;

    while (i < last) {

        if (bind_wildcard && !addr[i].opt.bind) {
        // 仔細(xì)分析一下,i的值就是那些沒(méi)有顯式的指定bind,將要被包含在wildcard中addr的數(shù)量
// 因?yàn)榕判蛑蟮腷ind在最前面,所以當(dāng)出現(xiàn)addr[i].opt.bind=0開(kāi)始,那么后面的addr.opt.bind都為0,
// 因?yàn)檫@是排序之后的數(shù)組。最后的一個(gè)元素的addr.opt.bind被nginx設(shè)置為了1,參考上面的代碼
            i++;
            continue;
        }
 // 如果能執(zhí)行到這里,那么有兩個(gè)條件 
// ① bind_wildcard=0,即不存在類似 listen *:80 的這種wildcard情況
// ② bind_wildcard = 1, 即存在wildcard,但是當(dāng)前的listen指令顯式的指定了bind屬性
        ls = ngx_http_add_listening(cf, &addr[i]);
        
        hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));
      
        ls->servers = hport;

        if (i == last - 1) {
            hport->naddrs = last;

        } else {
            hport->naddrs = 1;
            i = 0;
        }

        switch (ls->sockaddr->sa_family) {
        default: /* AF_INET */
            if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {
                return NGX_ERROR;
            }
            break;
        }

        addr++;
        last--;
    }

    return NGX_OK;
}

監(jiān)聽(tīng)端口

上面的源碼中有一個(gè)非常重要的函數(shù)ngx_http_add_listening(),這個(gè)函數(shù)實(shí)現(xiàn)了對(duì)端口的監(jiān)聽(tīng)。

static ngx_listening_t *
ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
{
    ngx_listening_t           *ls;
    ngx_http_core_loc_conf_t  *clcf;
    ngx_http_core_srv_conf_t  *cscf;
// 創(chuàng)建一個(gè)新的ngx_listening_t結(jié)構(gòu)體,表示監(jiān)聽(tīng)的端口
    ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen);
    
    ls->addr_ntop = 1;
//下面的一行代碼很重要。在accept成功之后會(huì)調(diào)用ls->handler函數(shù)
    ls->handler = ngx_http_init_connection;

    cscf = addr->default_server;
    ls->pool_size = cscf->connection_pool_size;
    ls->post_accept_timeout = cscf->client_header_timeout;

    clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];

    ls->logp = clcf->error_log;
    ls->log.data = &ls->addr_text;
    ls->log.handler = ngx_accept_log_error;

    ls->backlog = addr->opt.backlog;
    ls->rcvbuf = addr->opt.rcvbuf;
    ls->sndbuf = addr->opt.sndbuf;

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
    ls->accept_filter = addr->opt.accept_filter;
#endif

#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
    ls->deferred_accept = addr->opt.deferred_accept;
#endif

#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
    ls->ipv6only = addr->opt.ipv6only;
#endif

#if (NGX_HAVE_SETFIB)
    ls->setfib = addr->opt.setfib;
#endif

    return ls;
}

上面的函數(shù)又調(diào)用了ngx_create_listening(),源碼如下:

ngx_listening_t *
ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen)
{
    size_t            len;
    ngx_listening_t  *ls;
    struct sockaddr  *sa;
    u_char            text[NGX_SOCKADDR_STRLEN];
 // cycle->listening 數(shù)組中保存了監(jiān)聽(tīng)的端口的信息
    ls = ngx_array_push(&cf->cycle->listening);

    ngx_memzero(ls, sizeof(ngx_listening_t));
// ngx_listening_t結(jié)構(gòu)的sockaddr表示的是監(jiān)聽(tīng)的端口的結(jié)構(gòu)
    sa = ngx_palloc(cf->pool, socklen);
    ngx_memcpy(sa, sockaddr, socklen);
    ls->sockaddr = sa;
    ls->socklen = socklen;

//ngx_listening_t結(jié)構(gòu)的addr_text就是監(jiān)聽(tīng)的地址信息的字符串,包括端口
    len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1);
    ls->addr_text.len = len;

    switch (ls->sockaddr->sa_family) {

    case AF_INET:
         ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;
         break;
     }
// ngx_listening_t結(jié)構(gòu)的addr_text就是監(jiān)聽(tīng)的地址信息的字符串,包括端口
    ls->addr_text.data = ngx_pnalloc(cf->pool, len);
    ngx_memcpy(ls->addr_text.data, text, len);

    ls->fd = (ngx_socket_t) -1;
    ls->type = SOCK_STREAM;// 表示監(jiān)聽(tīng)的TCP

    ls->backlog = NGX_LISTEN_BACKLOG;
    ls->rcvbuf = -1;
    ls->sndbuf = -1;

#if (NGX_HAVE_SETFIB)
    ls->setfib = -1;
#endif

    return ls;
}

上面還牽涉到一個(gè)比較簡(jiǎn)單的函數(shù)ngx_sock_ntop(),這個(gè)函數(shù)的功能很簡(jiǎn)單,就是把sockaddr格式的ip地址轉(zhuǎn)換為一個(gè)字符串

/*
 *參數(shù)的含義:
 * sa : 要轉(zhuǎn)換為字符串的ip地址
 * text: 保存轉(zhuǎn)換之后的字符串
 * len: sa參數(shù)的長(zhǎng)度
 * port: 0或者1,表示是否將端口也轉(zhuǎn)換為字符串,如果為0則不轉(zhuǎn)換,若為1則轉(zhuǎn)換
 * 返回值:轉(zhuǎn)換的字符串的長(zhǎng)度
*/
size_t
ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, ngx_uint_t port)
{
    u_char               *p;
    struct sockaddr_in   *sin;
 
    switch (sa->sa_family) {
 
    case AF_INET:
 
        sin = (struct sockaddr_in *) sa;
        p = (u_char *) &sin->sin_addr;
 
        if (port) {
            p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d",
                             p[0], p[1], p[2], p[3], ntohs(sin->sin_port));
        } else {
            p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",
                             p[0], p[1], p[2], p[3]);
        }
 
        return (p - text);
    }
}

數(shù)據(jù)結(jié)構(gòu)

本文牽涉到的數(shù)據(jù)結(jié)構(gòu)比較多,整理如下:

typedef struct {
    /* ngx_http_in_addr_t or ngx_http_in6_addr_t */
    void        *addrs;  // ngx_http_in_addr_t 數(shù)組
    ngx_uint_t  naddrs; // 表示 addrs 數(shù)組元素的個(gè)數(shù)
} ngx_http_port_t;


typedef struct {
    in_addr_t                  addr; // 當(dāng)前監(jiān)聽(tīng)的地址
    ngx_http_addr_conf_t       conf; //參考下面的數(shù)據(jù)結(jié)構(gòu)
} ngx_http_in_addr_t;


typedef struct {
    /* the default server configuration for this address:port */
    ngx_http_core_srv_conf_t  *default_server; // 當(dāng)前address:port的default server
    ngx_http_virtual_names_t  *virtual_names; // 參考下面的數(shù)據(jù)結(jié)構(gòu)

#if (NGX_HTTP_SSL)
    ngx_uint_t                 ssl;   /* unsigned  ssl:1; */ // 表示listen指令是否使用了ssl
#endif
} ngx_http_addr_conf_t;


typedef struct {
// 參考下面的數(shù)據(jù)結(jié)構(gòu)
// name的hash指向address:port的hash
// name的wc_head指向address:port的wc_head
// name的wc_tail指向address:port的wc_tail
// 也就是說(shuō)把 address:port 的 hash,wc_head,wc_tail組合成一個(gè)ngx_hash_combined_t類型的數(shù)據(jù)結(jié)構(gòu),為后面做準(zhǔn)備.
// 因?yàn)楹竺媸褂玫降亩际?ngx_http_port_t 結(jié)構(gòu)體,不再使用address:port對(duì)應(yīng)的ngx_http_conf_addr_t結(jié)構(gòu)體
     ngx_hash_combined_t              names; 
 
     ngx_uint_t                       nregex;
     ngx_http_server_name_t          *regex;
} ngx_http_virtual_names_t;


typedef struct {
    ngx_hash_t            hash;
    ngx_hash_wildcard_t  *wc_head;
    ngx_hash_wildcard_t  *wc_tail;
} ngx_hash_combined_t;

喜歡本文的朋友們,歡迎長(zhǎng)按下圖關(guān)注訂閱號(hào)鄭爾多斯,更多精彩內(nèi)容第一時(shí)間送達(dá)


鄭爾多斯
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容