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