2013-07-11 46 views
6

我写了TCP中继服务器,它像对等路由器(超级节点)一样工作。两个TCP套接字之间的基于内核的(Linux)数据中继

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

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

但是服务器能够满足约2000 AB等对,即。 (基于socketA.recv()4000个插座...

有两种公知的在用户态数据流中继实现 - > socketB.send()socketB.recv() - > socketA.send()):

  • 使用的选择使用线程的/轮询函数(非阻塞方法)
  • /叉(阻塞方法)

我用线程在最坏的情况下服务器创建2 * 2000个线程!我不得不限制堆栈大小,它的工作原理,但它是正确的解决方案?

核心我的问题:

有没有办法避免活动数据在用户态的两个插槽之间的中继?

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

我也发现“套接字对”,但我不确定它是否适合我的目的。

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

一些额外的解释:

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

感谢格哈德·丽格我有提示:

我所知道的两个内核空间的方式,以避免读/写,recv的/在 用户空间发送:

  • sendfile
  • 拼接

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

dup2不会帮助在内核AFAIK做些什么。

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

相关链接:

+1

对于很多连接,线程数的组合,而['epoll的(4)'](http://linux.die.net/man/4/epoll)是可能是你应该看看。 –

+1

这一点,你可以使用类似[libev(http://software.schmorp.de/pkg/libev.html) – Hasturkun

+0

感谢。但是它仍然是用户空间中的主动中继。我相信存在一种被动的方法。服务器等待5秒超时的客户端ID,因此线程似乎是配对阶段的自然选择。 – nopsoft

回答

3

即使对于负载,因为微小的作为2000并发连接,我d永远不要使用线程。它们具有最高的堆栈和交换开销,仅仅因为确保您可以在任何地方被中断,而不是在只能在特定位置中断时总是更昂贵。只要使用epoll()和拼接(如果你的套接字是TCP,这似乎是这种情况),你会没事的。你甚至可以让epoll在事件触发模式下工作,在这种模式下你只注册一次fds。

如果你绝对要使用线程,使用每个CPU核心一个线程分布负载,但是如果你需要做到这一点,就意味着你正在玩的速度,其中的亲和力,每个CPU插座上RAM的位置等。 ..起着重要的作用,在你的问题中似乎不是这种情况。所以我假设在你的情况下单个线程已经足够了。

+0

感谢。最好使用EPOLLONESHOT或ADD/DEL?喜欢这里http://stackoverflow.com/questions/4173024/question-about-epoll-and-splice或http://rg4.net/archives/375.html? – nopsoft

+0

没试过EPOLLONESHOT,尽管它可能是有用的,也许一个优雅的替代EPOLLET。从标准的ADD/DEL开始,以限制我认为的复杂性,并在需要时尝试优化。 –