“Address already in use”几乎人人遇到过,标准解法是 SO_REUSEADDR;后来又有了 SO_REUSEPORT。这两个选项到底分别允许什么、不允许什么?这篇在 macOS 上把 bind 冲突的组合一个个跑出来,还看了 REUSEPORT 到底会不会分摊连接。
两边都设 都不设 :第二个 bind 同一个 127.0.0.1:P → EADDRINUSE 两边都设 REUSEADDR :第二个 bind 同一个 127.0.0.1:P → EADDRINUSE 两边都设 REUSEPORT :第二个 bind 同一个 127.0.0.1:P → 成功 两边都设 REUSEADDR+REUSEPORT :第二个 bind 同一个 127.0.0.1:P → 成功 只有一边设: 第一个设 REUSEPORT、第二个不设 → EADDRINUSE;第一个不设、第二个设 REUSEPORT → EADDRINUSE 第一个设 REUSEADDR、第二个不设 → EADDRINUSE;第一个不设、第二个设 REUSEADDR → EADDRINUSE
两个监听者共用同一个地址端口,要两边都设 REUSEPORT;只设 REUSEADDR 或只有一边设都是 EADDRINUSE。
重新 bind 时设 都不设 :netstat 里这个端口的状态 ['TIME_WAIT'];新套接字 bind + listen → EADDRINUSE 重新 bind 时设 REUSEADDR :netstat 里这个端口的状态 ['TIME_WAIT'];新套接字 bind + listen → 成功 重新 bind 时设 REUSEPORT :netstat 里这个端口的状态 ['TIME_WAIT'];新套接字 bind + listen → 成功
端口上留有 TIME_WAIT 时,重新 bind 需要 REUSEADDR(这台 macOS 上 REUSEPORT 也行)。
先 0.0.0.0 后 127.0.0.1,都不设 → EADDRINUSE
先 0.0.0.0 后 127.0.0.1,都设 REUSEADDR → 成功
先 127.0.0.1 后 0.0.0.0,都设 REUSEADDR → 成功
先 0.0.0.0 后 127.0.0.1,都设 REUSEPORT → 成功
两个都设 REUSEADDR 时,连 127.0.0.1:P 100 次,被谁接走:{'通配 0.0.0.0': 0, '具体 127.0.0.1': 100}(具体地址优先于通配)绑 0.0.0.0 和绑具体 IP 的两个套接字,设了 REUSEADDR 就能共存,连接给更具体的那个。
4 个监听者各 accept 到:[0, 0, 0, 400](合计 400)
这台 macOS 上 REUSEPORT 的 4 个监听者,400 个连接全部给了最后绑定的一个。
3 个 UDP 套接字各收到:[0, 0, 300](合计 300/300) 换成 30 个不同源端口的发送方、各发 10 个(共 300):各收到 [0, 0, 300](合计 300/300)
3 个 UDP 套接字同绑一个端口,无论 1 个还是 30 个发送方,300 个数据报都全部给最后绑定的那个。
只 REUSEADDR,绑 0.0.0.0 :3 次 bind → ['成功', 'EADDRINUSE', 'EADDRINUSE'];已绑定的套接字各收到 [10](共发 10 个) REUSEADDR+REUSEPORT,绑 0.0.0.0 :3 次 bind → ['成功', '成功', '成功'];已绑定的套接字各收到 [10, 10, 10](共发 10 个) 只 REUSEADDR,绑多播组地址 :3 次 bind → ['成功', '成功', '成功'];已绑定的套接字各收到 [10, 10, 10](共发 10 个)
多播要多个套接字都收到,需要两个选项都设,或把套接字绑到多播组地址。