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->addrs 为 NULL)分配内存。

为 u->addrs 分配连续的内存块,存储 total * nports 个 ngx_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


相关推荐
姜鱼问生21 分钟前
三层防火墙排查:Nginx 配好了浏览器还打不开
nginx·安全
小白的码BUG之路5 小时前
Docker -- 构建nginx镜像,启动vue项目
vue.js·nginx·docker
yume_sibai5 小时前
04-Nginx部署实战完全指南(前端部署 + 后端代理 + 监控告警 + 故障排查)
运维·前端·nginx
程序员清风15 小时前
Docker、Nginx 与 Kubernetes 部署 AI 后端服务
nginx·docker·kubernetes
吴声子夜歌1 天前
Nginx应用与运维——Nginx核心配置指令(一)
运维·nginx
谢亮_vipxieliang1 天前
一套 Compose 搭起完整开发环境:MySQL + Redis + Nginx
redis·mysql·nginx·docker
xbzb1 天前
Nginx 反向代理总 404?Location 匹配与 proxy_pass 尾斜杠避坑手册
linux·运维·nginx·虚拟主机
ShayneLee81 天前
Nginx只开一个端口!能做什么?(一)
运维·nginx
天衍四九-1 天前
Docker Compose企业实战系列(一):LNMP环境一键部署(Nginx\+MySQL\+PHP)
mysql·nginx·docker
PC2005-cloud2 天前
Nginx 学习笔记:Server 块配置详解,域名路由与多站点部署实战
nginx