在过去的几个月里,我一直在研究C ++和Java中的套接字服务器的一些实现。我用Java编写了一个小型服务器来处理&处理来自网站上托管的Flash应用程序的输入,我成功地编写了一个服务器,用于处理来自具有多个C ++玩家的2D游戏客户端的输入。我在一个项目中使用了TCP& UDP在另一个。现在,我确实有一些我在网上找不到的问题,我希望有些专家可以帮助我。 :)
假设我想在C ++中构建一个服务器来处理来自数千个独立和/或Web应用程序的输入,那么我应该如何设计我的服务器呢?到目前为止,我通常会创建一个新的&每个连接用户的唯一线程,但我怀疑这是要走的路。
此外,如何确定通过网络发送的数据包的布局;数据通常是通过网络以二进制或文本状态发送的吗?当您将数据发送到不同的媒体(例如C ++服务器到闪存应用程序)时,如何处理序列化对象?
最后,是否有任何易于使用的库,它通常用于支持可移植性(例如在Windows机器上开发和在Linux机器上部署)而不是boost asio。
谢谢。
答案 0 :(得分:9)
听起来你在这里有几个问题。我会尽力回答我能看到的内容。
<强> 1。我该如何处理网络服务器中的线程?
我会好好看看你在服务器生成的工作线程上做了哪些工作。为每个请求生成一个新线程并不是一个好主意......但如果并行请求的数量很少并且在每个线程上执行的任务都在快速运行,那么它可能不会造成任何损害。
如果你真的想以正确的方式做事,你可以拥有一个可配置/动态的线程池,它可以在工作线程空闲时回收工作线程。这样您就可以设置最大线程池大小。然后,您的服务器将处理池大小...然后进一步请求等待工作线程可用。
<强> 2。如何格式化数据包中的数据?
除非你正在开发一个全新的协议......这不是你真正需要担心的事情。除非您正在处理流媒体(或其他可以接受数据包丢失/损坏的应用程序),否则您可能不会在此应用程序中使用UDP。 TCP / IP可能是你最好的选择......这将决定你的数据包设计。
第3。我使用哪种格式进行序列化?
通过网络序列化数据的方式取决于哪种应用程序将消耗您的服务。二进制序列化通常更快,并且导致需要通过网络传输的数据量更少。使用二进制序列化的缺点是一种语言的二进制序列化可能在另一种语言中不起作用。因此,连接到服务器的客户端很可能必须使用您正在使用的语言编写。
XML序列化是另一种选择。这将需要更长的时间,并且需要通过网络传输更多的数据。使用XML序列化之类的好处是,您不会局限于可以连接到您的服务器并使用您的服务的客户端类型。
你必须选择最适合你需求的东西。
...玩弄不同的选项,找出最适合你的选择。希望你能找到比我在这里提到的更快,更可靠的东西。
答案 1 :(得分:6)
就服务器设计问题而言,我会说你是对的:尽管ONE-THREAD-PER-SOCKET是一种简单易用的方法,但它不是一种可行的方法,因为它不会像其它方式那样扩展服务器设计模式。
我个人喜欢COMMUNICATION-THREADS / WORKER-THREADS方法,其中动态数量的工作线程池处理生产者线程生成的所有工作。
在此模型中,池中将有许多线程等待从另一组处理网络I / O的线程生成的任务。
我发现了理查德史蒂文斯的UNIX Network Programming以及这种网络编程方法的惊人资源。而且,尽管它的名字,它在Windows环境中也非常有用。
关于数据包的布局(你应该发布一个不同的问题,因为这是一个完全不同的问题,在我看来),在选择TEXT和BINARY方法时会有权衡。
TEXT(即XML)可能更容易解析和记录,一般来说更简单,而BINARY协议应该在处理速度和网络数据包大小方面提供更好的性能,但是你必须处理更复杂的问题,如单词的ENDIANNES和类似的东西。
希望它有所帮助。
答案 2 :(得分:2)
虽然以前的答案提供了良好的方向,但为了完整性,我想指出线程不是绝对要求良好的套接字服务器性能。一些例子是here。可伸缩性的方法也很多 - 线程池,预分叉进程,服务器池等。
答案 3 :(得分:2)
1)最后,是否有任何易于使用的库,它通常用于支持可移植性(例如在Windows机器上开发和在Linux机器上部署)而不是boost asio。 < / p>
ACE库是另一种选择。它非常成熟(自90年代初以来一直存在)并得到广泛部署。 Boost ASIO网站Riverace上提供了与here进行比较的简短讨论。请记住,ACE必须长时间支持大量遗留平台,因此它不像Boost ASIO那样利用现代C ++功能。
2)假设我想在C ++中构建一个服务器来处理来自数千个独立和/或Web应用程序的输入,那么我应该如何设计我的服务器呢?到目前为止,我通常会创建一个新的&amp;连接的每个用户的唯一线程,但我怀疑这是要走的路。
有许多常用的方法,包括但不限于:每个连接的线程(您描述的方法)和线程池(Justin描述的方法) )。每个都有其优点和缺点。许多人都在考虑权衡取舍。一个很好的起点可能是Thread Pool Pattern维基百科页面上的链接。
Dan Kegel的“The C10K Problem”网页提供了许多关于提高可扩展性的有用说明。
3)另外,如何确定通过网络发送的数据包的布局;数据通常是通过网络以二进制或文本状态发送的吗?当您将数据发送到不同的媒体(例如C ++服务器到闪存应用程序)时,如何处理序列化对象?
我同意其他人一样,发送二进制数据通常会效率最高。 boost serialization库可用于将数据编组为二进制形式(以及文本)。成熟的二进制格式包括XDR和CDR。 CDR是CORBA使用的格式。公司ZeroC定义了ICE编码,该编码应该比CDR更有效。
有很多二进制格式可供选择。我的建议是至少通过阅读一些二进制格式来避免重新发明轮子,这样你就不会遇到这些现有二进制格式设计要解决的陷阱。
尽管如此,很多middleware已经存在,已经为您的大多数需求提供了罐装解决方案。例如,OpenSplice和OpenDDS都是OMG Data Distribution Service标准的实现。 DDS专注于通过publish-subscribe model有效地分发数据,而不是远程调用函数。我对OMG定义的技术比较熟悉,但我确信还有其他中间件实现可以满足您的需求。
答案 4 :(得分:1)
你仍然需要一个套接字来处理每个客户端,但想法是创建一个X套接字池(比如50),然后,当你接近(比如90%)消耗所有这些套接字时,创建另一个X套接字池。在某些时候,在客户端连接,发送数据和断开连接后,您的一些套接字将可供使用,您可以使用它们(谷歌套接字池用于此信息)
数据布局总是很困难。如果您的所有客户端和服务器都使用相同的硬件和操作系统,您可以以二进制格式发送数据,但那里有许多行程和陷阱(字节对齐位于列表的顶部)。发送格式化文本总是比较容易,但在带宽和处理能力方面肯定会更加昂贵,因为你必须在发送之前将格式从一个机器改为另一个文本,当然,再次在接收器处。
re:序列化,对不起,我帮不了你,也没有图书馆(我太过嵌入了很多这些)#/ p>
答案 5 :(得分:0)
关于服务器套接字和序列化(编组)。最重要的问题是增加套接字号是select中可读写的状态。我不是关于FD_SET的限制。这可以简单地解决。我是关于在处理评估套接字中可用数据时不在读取套接字中信令和问题数据累积的时间增长。因此,当处理器的角色有限时,解决方案可能甚至超出SW边界并需要多个处理器模型:一个读取和写入,N正在处理。在这种情况下,当select返回并发送到另一个处理单元时,应该读取所有可用的套接字数据。
传入数据也一样。
关于编组。粗略的二进制格式是优选的,因为性能。通过UNICODE方面的XML具有相同的问题。但是,...同志们,它不是简单地将长整数值或整数值复制到套接字流中。但在这种情况下,即使是htons,htonl也可以提供帮助(它以NW格式发送/接收,OS负责数据转换)。但是更安全的是在表示头之后发送数据,其中放置了大多数/最低有效位的外露格式,字节顺序和IEEE数据类型。这是有效的,我不是没有的情况。
亲切的问候并为每个人取得巨大成功。 西蒙康托尔