我写了一个TCP中继服务器,它像点对点路由器(超级节点)一样工作。
最简单的情况是两个打开的套接字之间的数据中继:
客户端A <--- > 服务器 <--- > 客户端B
然而,服务器必须为大约2000个这样的A-B对提供服务,即4000个套接字...。
在用户空间中有两种已知的数据流中继实现(基于socketA.recv() --> socketB.send()和socketB.recv() --> socketA.send()):
使用select/poll函数(非阻塞方法) 使用线程/分叉(阻塞方法)
我使用了线程,所以在最坏的情况下,服务器会创建2 * 2000个线程!我不得不限制堆栈大小,它能工作,但是这是正确的解决方案吗?
我的问题的核心是:是否有一种避免在用户空间中两个套接字之间进行活动数据中继的方法?
似乎有一种被动的方法。例如,我可以从每个套接字创建文件描述符,创建两个管道并使用dup2()——与标准输入/输出重定向相同的方法。然后两个线程就不用于数据中继,并且可以完成/关闭。
问题是服务器是否应该关闭套接字和管道,以及如何知道管道何时断开以记录此事实?
我还发现了"socket pairs",但我不确定它是否适用于我的目的。
您会建议什么解决方案以卸载用户空间并限制线程数?
一些额外的解释:
服务器有定义的静态路由表(例如,具有ID_B的ID_A-配对标识符)。客户端A连接到服务器并发送ID_A。然后服务器等待客户端B。当A和B配对时(两个套接字都打开),服务器开始数据中继。
客户端是对称NAT后面的简单设备,因此N2N协议或NAT穿越技术对他们来说太复杂了。
感谢Gerhard Rieger的提示:
我知道避免在用户空间中进行读写、recv/send有两种内核空间方法: sendfile 刮送 两者都有关于文件描述符类型的限制。 dup2不能帮助在内核中做某事,据我所知。
最简单的情况是两个打开的套接字之间的数据中继:
客户端A <--- > 服务器 <--- > 客户端B
然而,服务器必须为大约2000个这样的A-B对提供服务,即4000个套接字...。
在用户空间中有两种已知的数据流中继实现(基于socketA.recv() --> socketB.send()和socketB.recv() --> socketA.send()):
使用select/poll函数(非阻塞方法) 使用线程/分叉(阻塞方法)
我使用了线程,所以在最坏的情况下,服务器会创建2 * 2000个线程!我不得不限制堆栈大小,它能工作,但是这是正确的解决方案吗?
我的问题的核心是:是否有一种避免在用户空间中两个套接字之间进行活动数据中继的方法?
似乎有一种被动的方法。例如,我可以从每个套接字创建文件描述符,创建两个管道并使用dup2()——与标准输入/输出重定向相同的方法。然后两个线程就不用于数据中继,并且可以完成/关闭。
问题是服务器是否应该关闭套接字和管道,以及如何知道管道何时断开以记录此事实?
我还发现了"socket pairs",但我不确定它是否适用于我的目的。
您会建议什么解决方案以卸载用户空间并限制线程数?
一些额外的解释:
服务器有定义的静态路由表(例如,具有ID_B的ID_A-配对标识符)。客户端A连接到服务器并发送ID_A。然后服务器等待客户端B。当A和B配对时(两个套接字都打开),服务器开始数据中继。
客户端是对称NAT后面的简单设备,因此N2N协议或NAT穿越技术对他们来说太复杂了。
感谢Gerhard Rieger的提示:
我知道避免在用户空间中进行读写、recv/send有两种内核空间方法: sendfile 刮送 两者都有关于文件描述符类型的限制。 dup2不能帮助在内核中做某事,据我所知。
Man pages: splice(2), splice(2), vmsplice(2), sendfile(2), and tee(2).
Related links:
epoll(4)
的组合。 - Some programmer dude