【发布时间】:2019-01-06 07:28:39
【问题描述】:
开始之前
请不要将此问题标记为重复问题。 我已经看过 SO 上关于使用套接字编程处理多个客户端的大量帖子。 大多数人只推荐多线程,但我试图避免这条路径,因为我读过它有一些问题:
- 可扩展性差
- 开销大/效率低/内存不足
- 难以调试
我读过的任何专门讨论使用单线程的帖子要么有错误/没有答案,要么解释不清楚,比如人们说“只需使用select()!”
问题
我正在为服务器编写代码以处理多个(约 1000 个)客户端,但我无法弄清楚如何创建有效的解决方案。现在,我已经有了能够一次处理 1 个客户端的服务器代码。两者都是用 C 编写的;服务器在使用 WinSock 的 Windows 上,客户端在 Linux 上。
服务器和客户端使用send() 和阻止recv() 调用来回发送多个通信。编写这段代码非常简单,我不会在这里发布它,因为它很长,我怀疑有人会真正阅读所有这些。确切的实现也不重要,我只想谈谈高级伪代码。真正的困难是改变服务器来处理多个客户端。
已经有什么
我找到了一个关于如何创建处理多个客户端的 WinSock 服务器的不错的 PDF 教程,可以在此处找到它:WinSock Multiple Client Support。它在 C++ 中,但很容易转移到 C 中。
据我了解,服务器的运行方式如下:
while (running) {
Sleep(1000);
/* Accept all incoming clients and add to clientArray. */
for (client in clientArray) {
/* Interact with client */
if (recv(...) == "disconnect") {
/* Disconnect from client */
}
}
}
/* Close all connections. */
我看到使用这种方法的问题是您基本上一次只处理一个客户端(这很明显,因为您不是多线程),但是如果与每个客户端的交互呢?只需要发生一次?意思是,如果我只想来回发送一些数据并关闭连接怎么办? 此操作可能需要 5 秒到 5 分钟,具体取决于客户端连接的速度,因此其他客户端会在服务器处理客户端 5 分钟时阻止对服务器的 connect() 调用。 这似乎不是很有效,但也许最好的方法是实现一个等待队列,客户端被连接并被告知等待一段时间?我不确定,但这让我很好奇大型服务器如何同时向数千个客户端发送更新下载,以及我是否应该以同样的方式操作。
另外,如果服务器和客户端之间的send() 和recv() 需要一段时间(约1 分钟),是否有理由在主服务器循环中添加Sleep(1000) 调用?
我的要求
我想要的是一种在单线程服务器上处理多个客户端的解决方案,该解决方案对于大约 1000 个客户端来说足够高效。如果您告诉我 PDF 中的解决方案很好,那对我来说已经足够了(也许我只是太专注于效率。)
如果你觉得虐待狂,请给出答案,包括对实现的口头解释、服务器/客户端伪代码,甚至是服务器的小示例代码。)
提前致谢。
【问题讨论】:
-
Windows 特定:I/O 完成端口
-
关于您的第一个要点,我很难相信单线程解决方案会比多线程解决方案更好地扩展。在您使用多线程的那一刻,您会立即从一个扩展到系统中的核心数量。如果您谈论的是可扩展性和数千个客户端,您可能会考虑像this 这样的服务器。在其中之一上,多线程将使您的性能提高接近 64 比 1。
-
P.S. 不要犯我几年前工作的公司所犯的错误。他们的扩展解决方案是使用单线程并为每个内核启动一个服务器实例。由于很多原因,这根本无法很好地扩展。
-
我必须同意亚历克斯。如果您的服务器 (a) 基于 Windows,并且 (b) 无意偏离 (a),那么 I/O 完成端口就是猫须。坦率地说,它们是令人难以置信的,关于 SO 的几个答案,以及网络上记录的使用示例,都试图公正地传达它们真正的 omfg 有多棒。在谷歌上搜索“io 完成端口示例”值得您花半小时的时间。你不会后悔的;我保证。
-
谢谢大家,我会查找 I/O 完成端口
标签: c performance sockets server client