nginx源码分析(5)——监听socket初始化

本文详细介绍了nginx源码中监听socket的初始化过程,包括解析配置文件获取监听信息,初始化ngx_listening_t结构,以及打开和配置监听socket的步骤。关键涉及的数据结构有ngx_http_conf_port_t、ngx_http_conf_addr_t,以及ngx_listening_t在ngx_cycle_t中的使用。
摘要由CSDN通过智能技术生成

        在nginx源码分析(4)中,看到了nginx的事件模型,但其中没有介绍监听socket的初始化。而对于web server来说,需要通过监听socket来监听客户端的连接等。本篇将会具体介绍这方面的内容。还记得在前文介绍ngx_cycle_t结构时,它具有一个listening属性,是一个数组,存储所有监听socket,下面就来看看这些信息是什么时候添加的、以及如何初始化的。

1. 重要的数据结构

        1. ngx_http_conf_port_t

        监听端口配置信息,addrs是在该端口上所有监听地址的数组。

typedef struct {
    ngx_int_t                  family;
    in_port_t                  port;
    ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */
} ngx_http_conf_port_t;

        2. ngx_http_conf_addr_t

        监听地址配置信息,包含了所有在该addr:port监听的所有server块的ngx_http_core_srv_conf_t结构,以及hash、wc_head和wc_tail这些hash结构,保存了以server name为key,ngx_http_core_srv_conf_t为value的哈希表,用于快速查找对应虚拟主机的配置信息。

typedef struct {
    ngx_http_listen_opt_t      opt;

    ngx_hash_t                 hash;
    ngx_hash_wildcard_t       *wc_head;
    ngx_hash_wildcard_t       *wc_tail;

#if (NGX_PCRE)
    ngx_uint_t                 nregex;
    ngx_http_server_name_t    *regex;
#endif

    /* the default server configuration for this address:port */
    ngx_http_core_srv_conf_t  *default_server;
    ngx_array_t                servers;  /* array of ngx_http_core_srv_conf_t */
} ngx_http_conf_addr_t;

2. 监听socket初始化

        nginx把需要监听的socket用ngx_listening_t表示,存放在ngx_cycle_t的listening数组中。具体监听socket的初始化可以分为3个步骤:1. 解析配置文件、获取监听socket相关信息;2. 初始化监听socket;3. 打开并配置监听socket。下面我们分步骤来查看具体细节。

(1)解析配置文件,获取监听socket信息

        用于设置监听socket的指令主要有两个:server_name和listen。server_name指令用于实现虚拟主机的功能,会设置每个server块的虚拟主机名,在处理请求时会根据请求行中的host来转发请求。而listen就是设置监听socket的信息。这两个指令都是ngx_http_core_module的,首先看一下server_name指令的回调函数ngx_http_core_server_name,这个函数完成的功能很简单就是将server_name指令指定的虚拟主机名添加到ngx_http_core_srv_conf_t的server_names数组中,以便在后面对整个web server支持的虚拟主机进行初始化。

        1. ngx_http_core_server_name

static char *
ngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_srv_conf_t *cscf = conf;

    // 局部变量声明
    ……

    value = cf->args->elts;
    for (i = 1; i < cf->args->nelts; i++) {
        ch = value[i].data[0];

        // 一些异常的处理
        ……
        sn = ngx_array_push(&cscf->server_names); // 向server_names数组添加元素
        if (sn == NULL) {
            return NGX_CONF_ERROR;
        }
#if (NGX_PCRE)
        sn->regex = NULL;
#endif
        sn->server = cscf;
        if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) {
            sn->name = cf->cycle->hostname;
        } else {
            sn->name = value[i];
        }
        /* 前缀不是~就不是正则表达式,需要将server name转换为小写 */
        if (value[i].data[0] != '~') {
            ngx_strlow(sn->name.data, sn->name.data, sn->name.len);
            continue;
        }
#if (NGX_PCRE)
        // 处理server name是正则表达式的情况
        ……      
#else
        return NGX_CONF_ERROR;
#endif
    }
    return NGX_CONF_OK;
}

        2. ngx_http_core_listen

        下面看一下listen指令的回调函数ngx_http_core_listen。这个函数主要还解析listen指令中的socket配置选项,并保存这些值,在函数的最后会调用ngx_http_add_listen函数添加监听socket的信息。

    cscf->listen = 1;
        将core module的server config的listen置为1,表示该server块已经调用listen指令,设置了监听socket信息。如果listen等于0,即server块没有调用listen指令,后面会对监听信息进行默认初始化,比如监听的端口是80,地址是localhost等。

    // listen指令的参数
    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) {
        if (u.err) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "%s in \"%V\" of the \"listen\" directive",
                               u.err, &u.url);
        }

        return NGX_CONF_ERROR;
    }
        这段代码意图很简单,就是解析listen指令中的url,ip地址和端口号信息。

    ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

    ngx_memcpy(&lsopt.u.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
    lsopt.wildcard = u.wildcard;

    /* 将二进制的地址结构,转换为文本格式 */
    (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr,
                         NGX_SOCKADDR_STRLEN, 1);
        ngx_http_listen_opt_t用于存储listen socket的配置信息,比如rcvbuf、sndbuf、backlog等,就是一些基本的socket选项。后面的大部分代码主要是处理listen指令指定的配置选项进行初始化,比如:default_server、rcvbuf、sndbuf、backlog等,代码太冗长,这里不粘贴。

    if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) {
        return NGX_CONF_OK;
    }
        在函数的最后部分调用了ngx_http_add_listen添加监听socket信息。在具体介绍这个函数实现之前,先来看一下nginx是如何存储监听socket的地址信息的。

在ngx_http_core_main_conf_t中有ports属性,保存nginx监听的所有端口的信息。ports是ngx_http_conf_port_t类型的数组,而每个ngx_http_conf_port_t结构又具有addrs属性,它存放了对应端口上要监听的地址。addrs是ngx_http_conf_addr_t类型的数组,ngx_http_conf_addr_t结构包含在addr:port上监听的虚拟主机名及对应的配置信息。

ngx_http_core_main_conf_t

    |---> prots: 监听的端口号的数组

                |---> ngx_http_conf_port_t:端口号的配置信息

                               |---> addrs:在该端口号上,监听的所有地址的数组

                                            |---> ngx_http_conf_addr_t:地址配置信息,包含在该addr:port上的多个虚拟主机

                                                           |---> servers:在addr:port上的说个server块的配置信息ngx_http_core_srv_conf_t

                                                           |            |---> ngx_http_core_srv_conf_t

                     

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值