【问题标题】:Why should I use non-blocking or blocking sockets? [closed]为什么要使用非阻塞或阻塞套接字? [关闭]
【发布时间】:2012-05-26 03:01:06
【问题描述】:

首先,我必须问哪个州最好?例如实时 MMORPG 服务器。如果我为每个客户端创建一个线程而不是使用非阻塞套接字怎么办?或者如果我使用一个包含所有非阻塞套接字的线程怎么办?你能解释一下优点吗?

【问题讨论】:

    标签: c++ sockets winsock


    【解决方案1】:

    您的问题值得更长的讨论,但这里有一个简短的答案:

    • 使用阻塞套接字意味着任何一个线程中的任何时候都只能有一个套接字处于活动状态(因为它在等待活动时会阻塞)
    • 使用阻塞套接字通常比非阻塞套接字更容易(异步编程往往更复杂)
    • 您可以按照您的说明为每个套接字创建 1 个线程,但与非阻塞解决方案相比,线程有开销并且效率极低;
    • 使用非阻塞套接字,您可以处理更多的客户端:它可以在单个进程中扩展到数十万个 - 但代码变得有点复杂

    使用非阻塞套接字(在 Windows 上),您有几个选择:

    • 轮询
    • 基于事件
    • 重叠的 I/O

    重叠 I/O 将为您提供最佳性能(数千个套接字/进程),但代价是要正确理解和实施最复杂的模型。

    基本上,它归结为性能与编程复杂性。

    注意

    这里更好地解释了为什么使用线程/套接字模型是一个坏主意:

    在 Windows 中,创建大量线程非常低效,因为调度程序无法正确确定哪些线程应该接收处理器时间,哪些不应该。这与每个线程的内存开销相结合,意味着您将在操作系统级别耗尽内存(因为堆栈空间)和处理器周期(因为管理线程的开销),而在处理套接字连接的容量不足之前很久.

    【讨论】:

    • 如果你在一个80路的盒子上运行,你可以撕掉数百个线程没有问题。
    • 我的观点,诚然不是很好,是你不能只在 20 处画一条任意线,然后调用超出“坏”的任意数量的线程。
    • 您也不应该普遍谴责线程/套接字模型。并非所有东西都是 Web 服务器。
    • 需要坚定而明确地声明,这种'10-20线程'的业务是无稽之谈。实际上,任何 Java 程序都会有 10 个线程,而且肯定有服务器使用数十万个线程。
    • 作者写这篇文章时可能是这样,但我高度怀疑它仍然是这种情况,因为 >= W8 中改进了调度。我刚刚在同一台机器(16/32 核心)上使用 5000 个服务器线程 + 5000 个客户端线程(加上 5000 个额外的写入器线程)进行了尝试,每个连接有 1000 个乒乓连接(约 20 个字符)。效果很棒 - 客户端的平均运行时间为 52 秒,最短 44 秒,最长 62 秒。几乎没有线程停顿等情况,工作负载在线程之间得到了很好的共享。自己试试吧。
    【解决方案2】:

    我将继续记录在案,对于几乎除了玩具程序之外的任何东西,您当然应该使用非阻塞套接字。

    阻塞套接字会导致一个严重的问题:如果另一端的机器(或与它的连接的任何部分)在阻塞调用期间发生故障,您的代码将最终被阻塞,直到 IP 堆栈超时。在典型情况下,大约需要 2 分钟,这对于大多数用途来说是完全不可接受的。 1 中止阻塞调用的唯一方法是终止产生它的线程——但终止线程本身几乎总是不可接受的,因为在它之后清理并回收它的任何资源基本上是不可能的已分配。非阻塞套接字使得在需要时中止调用变得微不足道,无需对发出调用的线程做任何事情。

    如果您使用多进程模型, 可以使阻塞套接字工作得很好。在这里,您只需为每个连接生成一个全新的进程。该进程使用阻塞套接字,当/如果出现问题时,您只需终止整个进程。操作系统知道如何清理进程中的资源,因此清理不是问题。但是它仍然存在其他潜在问题:1)您几乎需要一个进程监视器来在需要时终止进程,以及 2)生成一个进程通常比仅仅创建一个套接字要昂贵得多。尽管如此,这可能是一个可行的选择,尤其是在以下情况下:

    1. 您一次处理少量连接
    2. 您通常会对每个连接进行大量处理
    3. 您只与本地主机打交道,因此与它们的连接既快速又可靠
    4. 您更关心优化开发而不是执行

    1. 好吧,从技术上讲,这不是唯一可能的方式,但大多数替代方案都比较难看——更具体地说,我认为当你添加代码以找出问题所在时,并且然后解决这个问题,你可能比只使用非阻塞套接字做了更多的工作。

    【讨论】:

    • 可以通过断开套接字与另一个线程上下文的连接来中止阻塞的套接字操作而不终止阻塞的线程。这将导致阻塞操作失败并显示错误代码,然后阻塞线程可以继续执行其他操作,例如正常清理。
    • 至少可以说这看起来相当激进。 Linux recv() 的手册说,如果没有可用的消息,它将在阻塞套接字上阻塞。我相信你只需要在收到之前select()poll(),你就不必担心这篇文章提到的任何事情。
    猜你喜欢
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-13
    • 2011-08-19
    • 1970-01-01
    • 2010-10-31
    相关资源
    最近更新 更多