在华为服务器上运行go的http服务时,看到监听的端口是ipv6
而在阿里云机器上,则是ipv4
先说结论
Go的 net.Listen() 函数,如果不强行指定 IPv4 或 IPv6 ,在双栈系统上(VPS 同时支持 IPv4 和 IPv6)默认只会监听 IPv6 地址。这不影响客户端使用 IPv4 地址来访问。
Linux相关
使用man 7 ipv6来查看如下,“The port space of IPv6 is shared with IPv4”,ipv4地址在ipv6上有map ::FFF:。
IPv4 connections can be handled with the v6 API by using the v4-mapped-on-v6 address type;
thus a program needs to support only this API type to support both protocols.
This is handled transparently by the address handling functions in the C library.
IPv4 and IPv6 share the local port space.
When you get an IPv4 connection or packet to a IPv6 socket,its source address will be mapped to v6 and it will be mapped to v6.
The address notation for IPv6 is a group of 8 4-digit hexadecimal numbers, separated with a ‘:’."::" stands for a string of 0 bits.
Special addresses are ::1 for loopback and ::FFFF: for IPv4-mapped-on-IPv6.
在 linux 上有个内核参数 net.ipv6.bindv6only ,默认为关闭状态,这样 IPv6 的 socket 也就可以解析映射到同一个网卡的 IPv4 请求了。如果服务需要同时提供 IPv4 和 IPv6 的访问能力,只需要监听一个 IPv6 的 socket 即可。如果不希望 IPv4 可以访问 IPv6 的服务,就把 net.ipv6.bindv6only 置为 1:
$ cat /proc/sys/net/ipv6/bindv6only
但是,开启了这个参数后,go服务依然可以使用 IPv4 的地址来访问。
Go语言
内核参数没有生效,问题多半是发生在 syscall 的调用上,祭出 strace大杀器:
|
|
可以看到在 220 行 Golang 把准备 listen 的 socket 选项置为了 0。查看代码src/net/ipsock_posix.go :
|
|
Go自己定义了 IPV6_V6ONLY 这个行为,至于这么做的原因在官方 Github 也有一些讨论:net: Listen is unfriendly to multiple address families, endpoints and subflows。
如何更准确地控制
在上层可以使用 http.ListenAndServe来选择,如:
如果觉得具体指定 IPv6地址太麻烦,可以用 net.Listen 重构 ListenAndServe ,在该函数里指定 network ,可选参数:
"tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
"udp", "udp4" (IPv4-only), "udp6" (IPv6-only),
"ip", "ip4" (IPv4-only), "ip6" (IPv6-only),
"unix", "unixgram" and "unixpacket"
常用:
tcp 自动适配,优先IPv6
tcp4 仅使用IPv4
tcp6 仅使用IPv6
示例如下:
|
|
客户端可以用下面的命令行检测 IPv6 服务:
插曲
如果你用 Chrome 访问 http://localhost:6666 这样的地址,可能会看到 ERR_UNSAFE_PORT 这样的错误页面。这个其实是因为 Chrome 的非安全端口限制。
像这样的端口,一共有 64 个:
|
|
本文发表于 0001-01-01,最后修改于 0001-01-01。
本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。