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


相关推荐
全栈工程师修炼指南29 分钟前
Nginx | stream content 阶段:UDP 协议四层反向代理浅析与实践
运维·网络·网络协议·nginx·udp
鹏北海1 小时前
micro-app 微前端项目部署指南
前端·nginx·微服务
全栈工程师修炼指南2 小时前
Nginx | stream content 阶段:TCP 协议四层反向代理浅析与实践
运维·网络·网络协议·tcp/ip·nginx
森焱森3 小时前
详解 Spring Boot、Flask、Nginx、Redis、MySQL 的关系与协作
spring boot·redis·python·nginx·flask
考琪5 小时前
Nginx打印变量到log方法
java·运维·nginx
消失的旧时光-19435 小时前
Nginx 是什么?为什么它不写在代码里?——从 0 认识 Nginx
运维·服务器·nginx
不像程序员的程序媛15 小时前
Nginx日志切分
服务器·前端·nginx
JoySSLLian21 小时前
手把手教你安装免费SSL证书(附宝塔/Nginx/Apache配置教程)
网络·人工智能·网络协议·tcp/ip·nginx·apache·ssl
一分半心动1 天前
宝塔面板lnmp架构,tp6框架网站伪静态
nginx·php
全栈工程师修炼指南1 天前
Nginx | stream 四层反向代理:SSL、PREREAD 阶段模块指令浅析与实践
运维·网络·网络协议·nginx·ssl