Sockets 两个TCP套接字之间基于内核(Linux)的数据中继

Sockets 两个TCP套接字之间基于内核(Linux)的数据中继,sockets,tcp,kernel,splice,Sockets,Tcp,Kernel,Splice,我编写了TCP中继服务器,它的工作原理类似于对等路由器(超级节点) 最简单的情况是两个打开的插座和它们之间的数据继电器: 客户端A服务器客户端B 然而,服务器必须服务大约2000个这样的A-B对,即4000个套接字 userland中有两种众所周知的数据流中继实现(基于socketA.recv()-->socketB.send()和socketB.recv()-->socketA.send()): 使用选择/轮询功能(非阻塞方法) 使用螺纹/叉子(分块法) 我使用线程,所以在最坏的情况下,服

我编写了TCP中继服务器,它的工作原理类似于对等路由器(超级节点)

最简单的情况是两个打开的插座和它们之间的数据继电器:

客户端A服务器客户端B

然而,服务器必须服务大约2000个这样的A-B对,即4000个套接字

userland中有两种众所周知的数据流中继实现(基于socketA.recv()-->socketB.send()和socketB.recv()-->socketA.send()):

  • 使用选择/轮询功能(非阻塞方法)
  • 使用螺纹/叉子(分块法)
我使用线程,所以在最坏的情况下,服务器会创建2*2000个线程!我必须限制堆栈大小,它可以工作,但这是正确的解决方案吗

我问题的核心是:

是否有办法避免userland中两个套接字之间的活动数据中继?

似乎有一种被动的方式。例如,我可以从每个套接字创建文件描述符,创建两个管道并使用dup2()-与stdin/out重定向相同的方法。然后,两个线程对于数据中继无效,可以完成/关闭。 问题是服务器是否应该关闭套接字和管道,以及如何知道管道何时断开以记录事实?

我也发现了“插座对”,但我不确定它是否适合我的用途

您建议什么解决方案来卸载用户区并限制线程数量?

一些额外的解释:

  • 服务器已经定义了静态路由表(例如ID_A和ID_B-成对标识符)。客户端A连接到服务器并发送ID_A。然后服务器等待客户端B。当A和B配对(两个套接字都打开)时,服务器启动数据中继
  • 客户端是对称NAT背后的简单设备,因此N2N协议或NAT遍历技术对它们来说太复杂了
多亏了格哈德·里格,我得到了提示:

我知道有两种内核空间方法可以避免读/写、recv/send-in 用户空间:

  • 发送文件
  • 拼接
两者都有关于文件描述符类型的限制

dup2将无助于在内核中执行某些操作,AFAIK

手册页:

相关链接:

  • (莱纳斯)

OpenBSD实现SO\u拼接:

  • relayd asiabsdcon2013/
Linux是否支持类似的功能,或者只有自己的内核模块才是解决方案?

  • 描述了SP-MOD
  • 描述了TCP拼接器

即使负载只有2000个并发连接,我也不会使用线程。它们具有最高的堆栈和交换开销,这仅仅是因为确保您可以在任何地方被中断总是比仅在特定位置被中断要昂贵。只需使用epoll()和splice(如果您的套接字是TCP,似乎是这样),您就可以了。您甚至可以使epoll在事件触发模式下工作,在该模式下您只注册一次FD


如果你绝对想使用线程,每个CPU核心使用一个线程来分散负载,但是如果你需要这样做,这意味着你正在以相似性、每个CPU插槽上的RAM位置等的速度进行游戏。。。扮演着重要的角色,在你的问题中似乎不是这样。因此,我假设在您的情况下,一个线程就足够了。

对于这么多的连接,几个线程的组合可能是您应该研究的内容。您可以使用类似于“谢谢”的内容。然而,它仍然是用户区的主动中继。我认为存在一种被动的方法。服务器等待客户端ID,超时5秒,因此线程似乎是配对阶段的自然选择。很好的简短比较:。我只是在每个线程中使用了阻塞recv()。不过,为了限制线程,我可以在一个线程或FASYNC中使用epoll()。最好使用epolonneshot或ADD/DEL?喜欢这里还是?从未尝试过EPOLLONESHOT,尽管它可能很有用,也许是EPOLLET的优雅替代品。从标准的ADD/DEL开始,以限制我认为的复杂性,并在需要时尝试稍后进行优化。