ngx_inet_add_addr

复制代码
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


相关推荐
米粉030516 小时前
深入剖析Nginx:从入门到高并发架构实战
java·运维·nginx·架构
静水楼台x20 小时前
nginx日志的一点理解
运维·nginx
欧先生^_^20 小时前
ingress-nginx 开启 Prometheus 监控 + Grafana 查看指标
nginx·grafana·prometheus
眠修1 天前
Nginx + Tomcat负载均衡群集
nginx·tomcat·负载均衡
瘦皮猴1 天前
golang context canceled异常排查
后端·nginx
hunter1271 天前
Nginx 事件驱动理解
nginx
fydw_7152 天前
生产环境中安装和配置 Nginx 以部署 Flask 应用的详细指南
运维·nginx·flask
xzh2 天前
问题:Nginx client_body_temp_path 文件会删除吗,删除时机?
nginx·架构
dessler2 天前
代理服务器-LVS的3种模式与调度算法
运维·服务器·网络·算法·nginx·tomcat·lvs
2501_911121232 天前
Nginx+Tomcat 负载均衡群集
nginx·tomcat·负载均衡