我需要设计一台服务器,可以同时通过TCP连接数百万个客户端。
由于服务器和客户端之间的数据流量较少,因此可以忽略带宽问题。
一个重要的要求是,每当服务器需要向任何客户端发送数据时,都应该使用现有的TCP连接,而不是向该客户端开启一个新的连接(因为客户端可能在防火墙后面)。
有人知道如何做到这一点,并且需要什么硬件/软件(成本最低)吗?
我需要设计一台服务器,可以同时通过TCP连接数百万个客户端。
由于服务器和客户端之间的数据流量较少,因此可以忽略带宽问题。
一个重要的要求是,每当服务器需要向任何客户端发送数据时,都应该使用现有的TCP连接,而不是向该客户端开启一个新的连接(因为客户端可能在防火墙后面)。
有人知道如何做到这一点,并且需要什么硬件/软件(成本最低)吗?
你们考虑采用哪种操作系统?
如果使用的是Windows操作系统,并且使用的是比Vista更高的版本,那么在单台机器上处理成千上万个连接不应该有问题。我曾经在一台配置较低的Windows Server 2003机器上运行过测试(参见链接:http://www.lenholgate.com/blog/2005/11/windows-tcpip-server-performance.html),很容易就实现了超过70,000个活动TCP连接。一些影响连接数量的资源限制已经在Vista上得到了显著提升(请参见此处:http://www.lenholgate.com/blog/2005/11/windows-tcpip-server-performance.html),因此您可能可以通过一小组机器来实现您的目标。但我不知道您需要在这些机器前面放置什么来路由这些连接。
Windows提供了一个称为I/O完成端口的功能(请参见:http://msdn.microsoft.com/en-us/magazine/cc302334.aspx),它允许您使用非常少的线程服务于多达数千个并发连接(我昨天正在运行测试,使用5000个连接饱和了与服务器的链接,并使用2个线程来处理I/O...)。因此,基本架构非常具有可扩展性。
如果您想进行一些测试,我在我的博客上提供了一些免费工具,可以使用成千上万的连接来测试一个简单的回显服务器(参见:1)和(2),同时还提供了一些免费代码,可帮助你入门(3)。
您的问题第二部分,从您的评论中可以看出,更加棘手。如果客户端的IP地址不断变化,并且你们之间没有提供NAT以给你一个一致的IP地址,那么他们的连接无疑会被终止并需要重新建立连接。如果客户端在其IP地址更改时检测到此连接中断,则可以重新连接服务器;如果不能,则建议客户端每隔一段时间轮询服务器以便检测连接丢失并重新连接。这里服务器无能为力,因为它无法预测新的IP地址,并且当它尝试发送数据时会发现旧的连接已经失败。而且请记住,一旦系统扩展到这个级别,您的问题才刚刚开始...这个问题与所谓的C10K问题有关。C10K页面列出了大量优秀资源,以解决当你尝试让数千个客户端连接到同一个服务器时会遇到的问题。
我之前了解过APE项目。它似乎是梦想成真。它可以在单个节点上支持高达100k并发客户端。将它们分布在10或20个节点上,您就可以为数百万人提供服务。非常适合RESTful应用程序。可能需要深入了解任何共享命名空间。其中一个缺点是这是一个独立的服务器,作为Web服务器的补充。当然,这个服务器是开源的,所以任何费用都与硬件/ISP相关。
你不能使用UDP。如果客户端发送请求,而你不立即回复,路由器将在30秒或更短时间内忘记反向路由,因此你的服务器将无法回复客户端。
TCP是唯一的选择,但它也会给你带来麻烦。大多数路由器会在几分钟后忘记路由和/或断开连接,因此你的客户端/服务器代码需要经常发送“保持活动”信号。
我建议设置一个“嗅探器”,以查看电话公司如何通过他们的“推送”技术与你的智能手机保持联系。复制他们正在做的任何事情,因为那些东西有效!
编辑:如下评论中所述,我最初的断言基于端口数量存在64K限制是不正确的,然而在socket句柄数量方面确实存在32K的限制,因此我的建议设计是有效的。
使用典型的TCP/IP服务器设计,你受到同时打开连接数量的限制。服务器有一个侦听端口,当客户端连接到它时,服务器进行接受调用,并为连接的其余部分创建一个新的Socket(随机端口)。
为了处理超过64K个并发连接,我认为您需要改用UDP。你只需要一个端口来侦听服务器,并且需要使用数据包中的32位客户端ID来管理连接,而不是为每个客户端单独分配一个端口。32位客户端ID可以是客户端的IP地址,客户端可以在已知的UDP端口上侦听从服务器返回的消息。该端口将是防火墙上唯一需要打开的端口。
采用这种方法,您唯一的限制是处理和响应UDP消息的速度。即使是稀疏的流量,在拥有数百万个客户端的情况下,也可能导致大量的峰值,并且如果您没有足够快地读取数据包,则输入队列将填满并且会开始丢包。Greg指出的C10K页面将为您提供解决策略。
accept()
调用生成更多的套接字句柄。 - Greg Hewgill