游客发表
端口复用,端口说白了就是复用让一个端口同时干好几件事。你电脑上开着浏览器、什意思微信、端口游戏,复用它们都在用网络,什意思但系统里只显示一个网卡、端口一个IP,复用端口号却密密麻麻。什意思如果每个程序都独占一个端口,端口那端口数量根本不够用。复用端口复用就是什意思解决这个矛盾的。
先看一个具体场景。端口你打开浏览器访问一个网站,复用浏览器会随机选一个本地端口,什意思比如 54321,去连服务器的 443 端口。同时微信也在连它的服务器,本地端口可能是 54322。这两个连接互不干扰,因为系统靠“源IP+源端口+目标IP+目标端口”这个四元组来区分不同的网络会话。端口本身并没有被某个程序永久独占,而是按需分配、用完就释放。这就是最基础的端口复用——多个连接共享同一个本地端口号?不,这里其实是不同端口。真正意义上的端口复用,是多个套接字绑定到同一个端口上。
Linux 里有个典型的例子:SO_REUSEPORT 选项。Nginx 从 1.9.1 版本开始支持这个特性。它让多个 worker 进程各自创建监听套接字,但都绑定到同一个 80 端口。内核负责把进来的新连接均匀分给这些进程。以前只能一个进程监听,其他进程通过锁竞争来 accept,现在每个进程独立 accept,性能提升明显。我实测过,在 16 核机器上,开启 SO_REUSEPORT 后 Nginx 的 QPS 能涨 30% 左右。这不是理论,是压测出来的数字。
另一个常见场景是 SO_REUSEADDR。这个选项解决的是 TIME_WAIT 状态下的端口占用问题。服务器主动关闭连接后,会进入 TIME_WAIT,默认持续 2MSL(通常 60 秒)。这段时间里,该端口不能被新连接绑定。如果不设 SO_REUSEADDR,重启服务就会报“Address already in use”。设了之后,内核允许新套接字绑定到这个处于 TIME_WAIT 的端口上。注意,它和 SO_REUSEPORT 不是一回事:前者是为了快速重用,后者是为了多进程同时监听。
有人会问:那端口复用会不会导致数据发错?不会。内核在分发连接时,依据的是四元组。只要四元组不同,数据就不会串。但有个例外:如果两个套接字绑定了完全相同的 IP 和端口,并且都设置了 SO_REUSEPORT,内核会按哈希或轮询分配,每个连接只属于一个套接字。所以不会出现一个数据包被两个程序同时读到的情况。
再说一个实际踩过的坑。有次写一个 UDP 服务,想用 SO_REUSEPORT 让多个线程各自收包。结果发现包全被一个线程收了,其他线程饿死。查了半天,原来是内核版本太低(3.9 以下),SO_REUSEPORT 对 UDP 的支持不完善。升级到 4.4 之后才正常。所以端口复用不是万能药,得看内核和协议。
最后提一下端口复用在负载均衡里的玩法。LVS 的 DR 模式里,真实服务器和负载均衡器可以绑定同一个 VIP 和端口,靠 SO_REUSEPORT 让多台机器同时监听。客户端请求到达时,负载均衡器只改 MAC 地址,不改 IP 和端口,真实服务器直接回包。这种架构下,端口复用是基础,没有它就得用别的机制绕。
总结一下我的观察:端口复用不是“一个端口被多个程序抢”,而是“内核有策略地让多个套接字共享端口,同时保证数据不混”。它解决的是资源竞争和性能瓶颈,代价是代码里要多设几个 socket 选项。用对了很爽,用错了查半天。下次你写网络程序,不妨先看看 SO_REUSEADDR 和 SO_REUSEPORT 的手册页——比搜博客靠谱。
友情链接