ngx_connection_local_sockaddr
1 定义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;#endifaddr0;if(c-local_socklen){switch(c-local_sockaddr-sa_family){#if(NGX_HAVE_INET6)caseAF_INET6:sin6(structsockaddr_in6*)c-local_sockaddr;for(i0;addr0i16;i){addr|sin6-sin6_addr.s6_addr[i];}break;#endif#if(NGX_HAVE_UNIX_DOMAIN)caseAF_UNIX:addr1;break;#endifdefault:/* AF_INET */sin(structsockaddr_in*)c-local_sockaddr;addrsin-sin_addr.s_addr;break;}}if(addr0){lensizeof(ngx_sockaddr_t);if(getsockname(c-fd,sa.sockaddr,len)-1){ngx_connection_error(c,ngx_socket_errno,getsockname() failed);returnNGX_ERROR;}c-local_sockaddrngx_palloc(c-pool,len);if(c-local_sockaddrNULL){returnNGX_ERROR;}ngx_memcpy(c-local_sockaddr,sa,len);c-local_socklenlen;}if(sNULL){returnNGX_OK;}s-lenngx_sock_ntop(c-local_sockaddr,c-local_socklen,s-data,s-len,port);returnNGX_OK;}ngx_connection_local_sockaddr 函数 获取并缓存当前连接的本地套接字地址 并可将其转换为文本格式的 IP和端口字符串。 它首先检查连接上是否已缓存有效的非“全零”地址 若没有则调用 getsockname 获取真实本地地址并保存到连接的内存池中 若调用者提供了输出字符串结构 s 则进一步将该地址格式化为可读字符串是否带端口由参数 port 控制。2 详解1 函数签名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 地址本身不附加端口。2 逻辑流程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;#endifaddr0;将 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(i0;addr0i16;i){addr|sin6-sin6_addr.s6_addr[i];}break;#endif#if(NGX_HAVE_UNIX_DOMAIN)caseAF_UNIX:addr1;break;#endifdefault:/* AF_INET */sin(structsockaddr_in*)c-local_sockaddr;addrsin-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 为 0addr 为 0表示未指定。3 获取地址if(addr0){lensizeof(ngx_sockaddr_t);if(getsockname(c-fd,sa.sockaddr,len)-1){ngx_connection_error(c,ngx_socket_errno,getsockname() failed);returnNGX_ERROR;}c-local_sockaddrngx_palloc(c-pool,len);if(c-local_sockaddrNULL){returnNGX_ERROR;}ngx_memcpy(c-local_sockaddr,sa,len);c-local_socklenlen;}#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(sNULL){returnNGX_OK;}s-lenngx_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 的方式 确保连接对象中存在一个真实有效的本地地址非全零 并能按要求生成文本表示。 这种“缓存惰性求值”的设计避免了不必要的系统调用 同时保证了地址与端口字符串的易用性。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2592828.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!