C++ 处理客户端连接的最有效方法(套接字编程)

C++ 处理客户端连接的最有效方法(套接字编程),c++,c,linux,sockets,unix,C++,C,Linux,Sockets,Unix,对于我在internet上看到的每个Linux/Unix套接字教程和示例,服务器端代码总是包含一个无限循环,每次都检查客户端连接。 例如: 是否有一种更有效的方法来构造服务器端代码,以使它不涉及无限循环,或者以无限的系统资源来编码无限循环?C++的 < P>可以查看。您还可以查看例如功能。还有 当然,即使使用这些异步方法,您的主程序仍然需要处于循环中,否则程序将退出。这些示例中的无限循环已经很有效。对accept()的调用是一个阻塞调用:只有当客户端连接到服务器时,函数才会返回。调用acce

对于我在internet上看到的每个Linux/Unix套接字教程和示例,服务器端代码总是包含一个无限循环,每次都检查客户端连接。 例如:


是否有一种更有效的方法来构造服务器端代码,以使它不涉及无限循环,或者以无限的系统资源来编码无限循环?C++的

< P>可以查看。您还可以查看例如功能。还有


当然,即使使用这些异步方法,您的主程序仍然需要处于循环中,否则程序将退出。

这些示例中的无限循环已经很有效。对
accept()
的调用是一个阻塞调用:只有当客户端连接到服务器时,函数才会返回。调用
accept()
函数的线程的代码执行将暂停,并且不会占用任何处理能力

可以将
accept()
看作是对
join()
的调用,或者类似于对互斥锁/锁/信号量的等待


当然,还有许多其他方法可以处理传入连接,但这些其他方法处理
accept()
的阻塞性质。此功能很难取消,因此存在非阻塞替代方案,允许服务器在等待传入连接时执行其他操作。一种替代方法是使用
select()
。其他替代方案的可移植性较差,因为它们涉及低级操作系统调用,通过回调函数、事件或操作系统处理的任何其他异步机制向连接发送信号…

无限循环用于维护服务器的运行状态,因此当接受客户端连接时,服务器不会立即退出,而是返回到侦听另一个客户端连接


listen()调用是一个阻塞调用——也就是说,它会一直等到收到数据。这是一种非常有效的方法,使用零系统资源(当然,在建立连接之前),通过使用触发事件(或硬件中断)的操作系统网络驱动程序来唤醒侦听线程。

当您实现侦听可能无限连接的服务器时,没有办法绕过某种无限循环。通常这根本不是问题,因为当套接字未标记为非阻塞时,对
accept()
的调用将阻塞,直到新连接到达。由于这种阻塞,不会浪费任何系统资源


其他提供类似基于事件的系统的库最终以上述方式实现。

这里是可用技术的一个很好的概述-。

除了已经发布的内容之外,很容易看到调试器的运行情况。您可以单步执行,直到执行accept()行,在该行上“单步”突出显示将消失,应用程序将继续运行-未到达下一行。如果在下一行中放置breadkpoint,它将在客户端连接之前不会启动。

我们需要遵循编写客户端-服务器编程的最佳实践。现在我能向你推荐的最好的指南是。在这种情况下,我们需要遵循一些具体的东西。我们可以使用select、poll或epoll。每种方法都有各自的优点和缺点

如果您使用最新的内核版本运行代码,那么我建议您使用EPOL。单击此处查看要理解的示例程序

如果您使用的是select、poll、epoll,那么您将被阻止,直到您获得一个事件/触发器,这样您的服务器就不会因为占用系统时间而运行到无限循环中

就我个人的经验而言,我认为epoll是最好的方法,因为我观察到我的服务器机器在拥有80k活动连接时的阈值在比较它将选择和轮询时非常低。在拥有80k活动连接时,我的服务器计算机的平均负载仅为3.2:)


在使用poll进行测试时,我发现我的服务器平均负载在达到30k活动客户端连接时上升到了7.8:(.

准确地说。从操作系统的角度来看(特别是Linux),当您调用
accept()
(阻塞调用)在您的进程中,如果没有传入连接,您的进程将进入睡眠状态:它的状态从TASK_RUNNING更改为TASK_(UN)可中断,它连接到内核的一个等待队列。然后调用调度程序来选择下一个要运行的进程-因此从操作系统的角度来看,不会浪费任何处理时间。当新的连接请求到达时,您的进程将被唤醒。最有效的(无论如何,不包括创建/终止/加入),线程化代码被写成无限循环,在顶部某处有一个阻塞调用。不要担心:)第二段基本上是不正确的。listen()不等待,也不接收数据。accept()等待,但它也不接收数据,它接收连接。这不是通过硬件中断或网络驱动程序实现的,而是通过TCP/IP堆栈和积压队列实现的。