ngx_inet_add_addr

发布于:2025-03-24 ⋅ 阅读:(142) ⋅ 点赞:(0)
static ngx_int_t
ngx_inet_add_addr(ngx_pool_t *pool, ngx_url_t *u, struct sockaddr *sockaddr,
    socklen_t socklen, ngx_uint_t total)
{
    u_char           *p;
    size_t            len;
    ngx_uint_t        i, nports;
    ngx_addr_t       *addr;
    struct sockaddr  *sa;

    nports = u->last_port ? u->last_port - u->port + 1 : 1;

    if (u->addrs == NULL) {
        u->addrs = ngx_palloc(pool, total * nports * sizeof(ngx_addr_t));
        if (u->addrs == NULL) {
            return NGX_ERROR;
        }
    }

    for (i = 0; i < nports; i++) {
        sa = ngx_pcalloc(pool, socklen);
        if (sa == NULL) {
            return NGX_ERROR;
        }

        ngx_memcpy(sa, sockaddr, socklen);

        ngx_inet_set_port(sa, u->port + i);

        switch (sa->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            len = NGX_INET6_ADDRSTRLEN + sizeof("[]:65536") - 1;
            break;
#endif

        default: /* AF_INET */
            len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
        }

        p = ngx_pnalloc(pool, len);
        if (p == NULL) {
            return NGX_ERROR;
        }

        len = ngx_sock_ntop(sa, socklen, p, len, 1);

        addr = &u->addrs[u->naddrs++];

        addr->sockaddr = sa;
        addr->socklen = socklen;

        addr->name.len = len;
        addr->name.data = p;
    }

    return NGX_OK;
}

ngx_inet_add_addr 函数的作用是将解析后的网络地址及端口信息存储到 ngx_url_t 结构中


函数签名

static ngx_int_t ngx_inet_add_addr(
    ngx_pool_t *pool, 
    ngx_url_t *u, 
    struct sockaddr *sockaddr,
    socklen_t socklen, 
    ngx_uint_t total
);

返回值

类型 ngx_int_t

  • NGX_OK(成功):地址信息已正确存储

  • NGX_ERROR(失败):内存分配失败或其他错误


参数

ngx_pool_t *pool

Nginx 内存池指针,用于动态分配内存


ngx_url_t *u

指向 ngx_url_t 结构的指针,存储解析后的 URL/地址信息


struct sockaddr *sockaddr

原始 socket 地址结构


socklen_t socklen

sockaddr 结构的实际长度(以字节为单位)

区分 IPv4(sizeof(struct sockaddr_in))和 IPv6(sizeof(struct sockaddr_in6))的地址结构长度


ngx_uint_t total

指定需要预分配的地址数量(通常为 1)


nports = u->last_port ? u->last_port - u->port + 1 : 1;

根据配置的端口范围计算需要生成的端口数量

u->port:起始端口号(例如 80

u->last_port:结束端口号(例如 82)。如果未配置端口范围,其值为 0

  • 如果 u->last_port 不为 0 (即配置了端口范围,如 80-82):
    • 端口数量 = last_port - port + 1
      (例如 82 - 80 + 1 = 3,对应端口 80, 81, 82)。
  • 如果 u->last_port 为 0 (即单个端口,如 80):
    • 端口数量 = 1

此时 u->last_port=0    nports=1

    if (u->addrs == NULL) {
        u->addrs = ngx_palloc(pool, total * nports * sizeof(ngx_addr_t));
        if (u->addrs == NULL) {
            return NGX_ERROR;
        }
    }

 

仅在首次调用时(u->addrsNULL)分配内存。

u->addrs 分配连续的内存块,存储 total * nportsngx_addr_t 结构

u->addrs 是一个动态数组

存储解析后的地址列表

  • 保存所有解析成功的网络地址(IP + 端口)的结构化信息。
  • 每个元素是一个 ngx_addr_t 结构,对应一个具体的地址与端口组合

total单个地址族(IPv4/IPv6)的地址数量

nports单个地址需要绑定的端口数量

    for (i = 0; i < nports; i++) {
        sa = ngx_pcalloc(pool, socklen);
        if (sa == NULL) {
            return NGX_ERROR;
        }

为每个端口创建独立的 socket 地址结构 ,确保不同端口的地址信息互不干扰

ngx_memcpy(sa, sockaddr, socklen);

 将原始 socket 地址模板复制到新分配的内存中 ,确保每个端口拥有独立的地址结构

ngx_inet_set_port(sa, u->port + i);

 ngx_inet_set_port(sa, u->port + i); 的作用是 为当前处理的端口设置正确的端口号 ,确保每个地址结构(sockaddr)拥有唯一的端口

 


进入 ngx_inet_set_port

void
ngx_inet_set_port(struct sockaddr *sa, in_port_t port)
{
    struct sockaddr_in   *sin;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6  *sin6;
#endif

    switch (sa->sa_family) {

#if (NGX_HAVE_INET6)
    case AF_INET6:
        sin6 = (struct sockaddr_in6 *) sa;
        sin6->sin6_port = htons(port);
        break;
#endif

#if (NGX_HAVE_UNIX_DOMAIN)
    case AF_UNIX:
        break;
#endif

    default: /* AF_INET */
        sin = (struct sockaddr_in *) sa;
        sin->sin_port = htons(port);
        break;
    }
}

此时会进入以下  分支

    default: /* AF_INET */
        sin = (struct sockaddr_in *) sa;
        sin->sin_port = htons(port);
        break;

 AF_INET 情况下的设置 port 方式


接下来: 

        default: /* AF_INET */
            len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;

此时 len(=21) = NGX_INET_ADDRSTRLEN(=15) + sizeof(":65535")(=7) - 1

 

NGX_INET_ADDRSTRLEN

  • Pv4 地址的最大字符串长度(例如 "255.255.255.255" 占 15 字符)

sizeof(":65535") - 1

  • 计算字符串 ":65535" 的字符数(不包含终止符 \0)。
  • sizeof(":65535") 返回 7(包含 \0),减 1 后得到 6(即 ":" + "65535" 的长度)

len 的值

表示 IPv4 地址和端口的字符串最大需要 21 字节

        p = ngx_pnalloc(pool, len);
        if (p == NULL) {
            return NGX_ERROR;
        }

 分配内存 len 个字节

len = ngx_sock_ntop(sa, socklen, p, len, 1);

 将二进制 socket 地址转换为可读的字符串形式 ,并记录实际写入的字符数

此时 p=0.0.0.0:80

addr = &u->addrs[u->naddrs++];

 获取数组当前元素地址,并递增计数器

        addr->sockaddr = sa;
        addr->socklen = socklen;

        addr->name.len = len;
        addr->name.data = p;

 设置 这个元素的各个字段

return NGX_OK;

 返回 NGX_OK


 


网站公告

今日签到

点亮在社区的每一天
去签到