Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

为 TCP Listener 添加 SO_REUSEPORT 的选项 #654

Open
iBug opened this issue Jan 27, 2025 · 0 comments
Open

为 TCP Listener 添加 SO_REUSEPORT 的选项 #654

iBug opened this issue Jan 27, 2025 · 0 comments

Comments

@iBug
Copy link

iBug commented Jan 27, 2025

功能请求(Feature Request)

用途

我在某机器上需要使用 Nginx 向多个端口(listen addr)提供 HTTP(S) 服务,同时在 127.0.0.1:443, 127.0.0.2:443, ..., 127.11.45.14:443 等一系列 loopback 地址上通过修改 /etc/hosts 的方式向本机进程使用 sniproxy 提供 SNI proxy 服务。具体的配置是,Nginx 使用 listen 443listen [::]:443 监听在“零地址”(INADDR_ANY)上,sniproxy 则根据线路监听在具体的单个地址上(如 127.0.0.1:443)。

目前我在 Nginx 和 sniproxy 上均对 listen 语句开启了 reuseport,此时它们互不冲突:本机进程如果连接到了有 sniproxy 在 listen 的地址上,则能正常访问 sniproxy 服务;否则访问到 nginx。如果不开启 reuseport,则一个监听零地址的 socket 会阻止其他监听非零地址但端口相同的 socket(sniproxy 得到 EADDRINUSE);如果开启 reuseport,则这两种 socket 可以同时 listen,且非零地址的优先级更高。

由于 gost 未开启 reuseport,我无法在此场景下从 sniproxy 无缝迁移至 gost。

Workaround

将 gost 监听在 444 端口上,然后用 iptables 把本地地址转发一下:

iptables -t nat -A OUTPUT -d 127.0.0.0/8 -p tcp --dport 443 -j DNAT --to-destination :444

其中 -d 后面的参数根据实际情况修改。

关于零地址和非零地址

代码验证

Kernel 代码量巨大过于复杂,我没有详细了解,但是从 inet_reuseport_add_sock 等函数中可以看出,kernel 有多处代码是专门处理了这种情况的,结合下述实验验证,我相信我对“非零地址的优先级高于零地址”的判断是正确的。

实验验证

将以下脚本保存到 server_1.py 中,然后复制一份到 server_2.py 并将 bind 的地址改成 127.0.0.1。

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
server.bind(("0.0.0.0", 6666))
server.listen(1)
print(f"Listening")
conn, addr = server.accept()
print(f"Connection from {addr}")
data = conn.recv(1024).decode('utf-8').rstrip()
print(data)
conn.close()
server.close()

同时启动两个 server 并向 127.0.0.1:6666 发送消息,例如:

nc 127.0.0.1 6666 <<< Hello

反复实验可发现,仅有 server_2.py 收到了 nc 发来的 TCP 连接;仅当 server_2.py 未启动时,server_1.py 才能收到连接。


注:我非常认同您在此处的 comment:

SO_REUSEPORT本身是为了提高服务端处理能力,让同一个端口可以运行多个服务实例,并不是为了解决端口冲突

但它与本 issue 仍然不同。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant