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


相关推荐
IT菜鸟程10 小时前
Ubuntu 下 apt 版 Nginx 升级为源码编译版本完整操作指南(1.24.0 → 1.30.4 实战)
linux·nginx·ubuntu
sbjdhjd12 小时前
从 7 字符文件名拼接到二维数组取值:PHP 无参函数限制下的受控靶场复盘 | (7字符绕过)
网络·nginx·安全·网络安全·云计算·php·apache
NGINX开源社区14 小时前
NGINX Gateway Fabric 2.6:F5 WAF for NGINX 集成 Gateway API
nginx·gateway·fabric
小白羊丨1 天前
HTTP/2 如何缓解 HTTP/1.1 阻塞;长连接对 Nginx 有什么影响?
网络协议·nginx·http
xixiaoyunya2 天前
Docker 容器化部署实战:从零搭建一套完整的 Nginx + Node.js + MySQL + Redis 项目环境
nginx·docker·node.js
SendTomo2 天前
【技术交流】从0到1构建一个汉字学习平台:汉字吧(hanzi8.com)的技术架构猜想
redis·mysql·nginx·php·个人开发
滨哥GPT2 天前
Codex新增文件上传后为什么小文件正常,大文件总失败?Multipart、Body Limit与Nginx限制排查
nginx·ai编程·文件上传·codex·multipart
come112343 天前
Nginx `location` 配置说明(后端开发版)
运维·nginx
PHP实战开发录3 天前
PHP文件上传成功为什么页面却找不到
开发语言·nginx·php·开发
「、皓子~4 天前
海狸IM 2.1 私有化部署从零到一:中间件、微服务与 Nginx 全流程
nginx·flutter·微服务·中间件·开源软件·im·海狸im