6

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

最简单的情况是两个打开的套接字和它们之间的数据中继:

客户端A <---> 服务器<---> 客户端B

然而,服务器必须为大约 2000 个这样的 AB 对提供服务,即。4000个插座...

用户空间中有两个众所周知的数据流中继实现(基于socketA.recv () --> socketB.send()socketB.recv() --> socketA.send()):

  • 使用选择/轮询功能(非阻塞方法)
  • 使用线程/分叉(阻塞方法)

我使用了线程,所以在最坏的情况下,服务器会创建 2*2000 个线程!我不得不限制堆栈大小并且它有效,但它是正确的解决方案吗?

我的问题的核心:

有没有办法避免用户空间中两个套接字之间的活动数据中继?

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

我还找到了“套接字对”,但我不确定它是否符合我的目的。

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

一些额外的解释:

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

感谢 Gerhard Rieger,我得到了提示:

我知道两种内核空间方法可以避免在用户空间中读/写、接收/发送:

  • 发送文件
  • 拼接

两者都有关于文件描述符类型的限制。

dup2 将无助于在内核中做某事,AFAIK。

手册页:splice(2) splice(2) vmsplice(2) sendfile(2) tee(2)

相关链接:

4

2 回答 2

6

OpenBSD 实现 SO_SPLICE:

Linux 是否支持类似的东西或只有自己的内核模块是解决方案?

于 2013-07-15T12:07:11.247 回答
3

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

如果您绝对想使用线程,请为每个 CPU 内核使用一个线程来分散负载,但如果您需要这样做,则意味着您正在以亲和性、每个 CPU 插槽上的 RAM 位置等的速度播放...重要角色,在您的问题中似乎并非如此。所以我假设在你的情况下一个线程就足够了。

于 2013-07-15T13:47:46.363 回答