5分钟搞定AI文本生成:oobabooga一键安装完全指南
2026/5/7 22:50:51
ngx_connection_local_sockaddr 函数 定义在 ./nginx-1.24.0/src/core/ngx_connection.cngx_int_tngx_connection_local_sockaddr(ngx_connection_t*c,ngx_str_t*s,ngx_uint_tport){socklen_tlen;ngx_uint_taddr;ngx_sockaddr_tsa;structsockaddr_in*sin;#if(NGX_HAVE_INET6)ngx_uint_ti;structsockaddr_in6*sin6;#endifaddr=0;if(c->local_socklen){switch(c->local_sockaddr->sa_family){#if(NGX_HAVE_INET6)caseAF_INET6:sin6=(structsockaddr_in6*)c->local_sockaddr;for(i=0;addr==0&&i<16;i++){addr|=sin6->sin6_addr.s6_addr[i];}break;#endif#if(NGX_HAVE_UNIX_DOMAIN)caseAF_UNIX:addr=1;break;#endifdefault:/* AF_INET */sin=(structsockaddr_in*)c->local_sockaddr;addr=sin->sin_addr.s_addr;break;}}if(addr==0){len=sizeof(ngx_sockaddr_t);if(getsockname(c->fd,&sa.sockaddr,&len)==-1){ngx_connection_error(c,ngx_socket_errno,"getsockname() failed");returnNGX_ERROR;}c->local_sockaddr=ngx_palloc(c->pool,len);if(c->local_sockaddr==NULL){returnNGX_ERROR;}ngx_memcpy(c->local_sockaddr,&sa,len);c->local_socklen=len;}if(s==NULL){returnNGX_OK;}s->len=ngx_sock_ntop(c->local_sockaddr,c->local_socklen,s->data,s->len,port);returnNGX_OK;}ngx_connection_local_sockaddr 函数 获取并缓存当前连接的本地套接字地址, 并可将其转换为文本格式的 IP(和端口)字符串。 它首先检查连接上是否已缓存有效的非“全零”地址, 若没有则调用 `getsockname` 获取真实本地地址并保存到连接的内存池中; 若调用者提供了输出字符串结构 `s`, 则进一步将该地址格式化为可读字符串(是否带端口由参数 `port` 控制)。ngx_int_tngx_connection_local_sockaddr(ngx_connection_t*c,ngx_str_t*s,ngx_uint_tport)返回值 NGX_OK:操作成功完成。 NGX_ERROR:操作失败 调用者通常通过检查返回值是否为 NGX_OK 来判断是否成功。参数1 ngx_connection_t *c 指向 ngx_connection_t 结构的指针。 代表一个客户端的 TCP 或 Unix 域套接字连接。 c->fd:套接字文件描述符,用于调用 getsockname 获取本地地址 通过传入此连接对象,函数即可获知要对哪个套接字进行操作,以及将结果缓存在哪里参数2 ngx_str_t *s 双重用途(输入与输出): 输入: 调用者预先分配好缓冲区,s->data 指向缓冲区的起始地址,s->len 存放该缓冲区的大小(字节数)。 输出: 函数内部调用 ngx_sock_ntop 将本地套接字地址格式化为可读字符串, 并修改 s->len 为实际写入的字符数(不包含结尾的 \0)。 此时 s 成为一个合法的 Nginx 字符串,可被其它 Nginx 模块直接使用。 若为 NULL: 表示调用者仅希望确保本地地址已被缓存,不需要获取字符串形式。 此时函数在校验/填充完缓存地址后直接返回 NGX_OK。参数3 ngx_uint_t port 作用: 控制由 ngx_sock_ntop 生成的字符串是否包含端口号。 值为 1(或非零): 格式化为 IP:PORT 的形式,例如 192.168.1.1:80 或 [::1]:8080。 值为 0: 仅返回 IP 地址本身,不附加端口。1 局部变量 2 已缓存本地地址 3 获取地址 4 无需字符串 5 返回成功1 局部变量{socklen_tlen;ngx_uint_taddr;ngx_sockaddr_tsa;structsockaddr_in*sin;#if(NGX_HAVE_INET6)ngx_uint_ti;structsockaddr_in6*sin6;#endifaddr=0;将 addr 初始化为 0, 表示“默认认为本地地址是全零(未指定)”。 后续会通过检查已缓存的地址来修改它。2 已缓存本地地址if(c->local_socklen){switch(c->local_sockaddr->sa_family){#if(NGX_HAVE_INET6)caseAF_INET6:sin6=(structsockaddr_in6*)c->local_sockaddr;for(i=0;addr==0&&i<16;i++){addr|=sin6->sin6_addr.s6_addr[i];}break;#endif#if(NGX_HAVE_UNIX_DOMAIN)caseAF_UNIX:addr=1;break;#endifdefault:/* AF_INET */sin=(structsockaddr_in*)c->local_sockaddr;addr=sin->sin_addr.s_addr;break;}}#1 判断连接对象 c 中是否已经保存了本地地址的长度(local_socklen 非 0)。 若已保存,说明之前已成功获取过本地地址,可以直接利用缓存; 若为 0,说明尚未获取,需要调用 getsockname。#2 根据已缓存地址的协议族(sa_family)进入不同的分支,提取有效地址判断标志 addr。#2-1 如果支持 IPv6 并且协议族是 AF_INET6, 则将已缓存地址强制转换成 sockaddr_in6 指针,以便访问 16 字节的 IPv6 地址部分。 遍历 IPv6 地址的 16 个字节。 循环条件是 addr == 0 && i < 16, 即只要 addr 仍为 0(说明前面检查的字节全为 0)且未遍历完 16 个字节,就继续。 每次循环将当前字节 sin6->sin6_addr.s6_addr[i] 按位或到 addr 上。 如果 16 个字节全部为 0,则循环结束后 addr 仍为 0; 只要任何一字节非零,addr 就变为非零,后续循环会因为 addr == 0 为假而提前结束,节省 CPU。#2-2 对于 Unix 域套接字地址(AF_UNIX), 不存在“全零未指定”的概念,直接设置 addr = 1 表示地址有效,无需再次获取。#2-3 其他情形(默认)即 IPv4。 将缓存地址转为 sockaddr_in,然后将其 32 位网络字节序的地址值赋给 addr。 如果该地址是 INADDR_ANY(即 0.0.0.0),则 s_addr 为 0,addr 为 0,表示未指定。3 获取地址if(addr==0){len=sizeof(ngx_sockaddr_t);if(getsockname(c->fd,&sa.sockaddr,&len)==-1){ngx_connection_error(c,ngx_socket_errno,"getsockname() failed");returnNGX_ERROR;}c->local_sockaddr=ngx_palloc(c->pool,len);if(c->local_sockaddr==NULL){returnNGX_ERROR;}ngx_memcpy(c->local_sockaddr,&sa,len);c->local_socklen=len;}#1 如果经过上述检查后 addr 仍为 0,意味着两种情况: 1 从未获取过本地地址(local_socklen == 0); 2 已缓存的地址是通配地址(全零),需要获取真正的本地地址。此时通过 getsockname 获取。#2 设置 len 为 ngx_sockaddr_t 联合体的大小, 作为传递给 getsockname 的缓冲区最大长度。#3 调用系统 API getsockname,获取文件描述符 c->fd 关联的本地套接字地址。 地址被写入 sa.sockaddr 所在的内存区域,len 被设置为实际地址长度。 如果调用失败(返回 -1), 用 ngx_connection_error 记录错误日志, 并返回 NGX_ERROR 表示失败。#4 从当前连接的内存池 c->pool 中分配 len 字节内存,用于持久保存该地址。 这样后续对同一连接的调用可直接复用缓存, 若内存分配失败(返回 NULL),则返回 NGX_ERROR。#5 将栈上临时变量 sa 中刚获取的地址数据复制到新分配的内存 c->local_sockaddr 中,完成缓存。#6 将地址长度记录下来,标记缓存有效。 下次调用此函数时,if (c->local_socklen) 将成立并直接使用缓存。4 无需字符串if(s==NULL){returnNGX_OK;}s->len=ngx_sock_ntop(c->local_sockaddr,c->local_socklen,s->data,s->len,port);#1 如果调用者没有提供输出字符串的结构(s == NULL), 表示它只希望确保本地地址已缓存,不需要转换成文本。 此时直接返回 NGX_OK,函数的核心任务已完成。#2 调用 ngx_sock_ntop 函数,将已缓存的套接字地址转换为可读文本, 写入 s->data 指向的预分配缓冲区。 传入的 s->len 是缓冲区大小(输入), 函数返回实际写入的字符数(不含结尾 \0), 并将其重新赋值给 s->len,使 s 成为一个合法的 ngx_str_t。 参数 port 控制是否在地址后附加 :端口号。5 返回成功returnNGX_OK;}所有操作成功完成,返回 NGX_OK。总结逻辑流程: 函数通过先检查缓存、再按需调用 getsockname 的方式, 确保连接对象中存在一个真实有效的本地地址(非全零), 并能按要求生成文本表示。 这种“缓存+惰性求值”的设计避免了不必要的系统调用, 同时保证了地址与端口字符串的易用性。