代码之家  ›  专栏  ›  技术社区  ›  Marlon

在select()和recv之间使用msg_peek的效率。异步的

  •  2
  • Marlon  · 技术社区  · 14 年前

    我想知道检查传入数据(异步)时最有效的方法是什么。假设我有500个连接。我有3个场景(我可以想到):

    1. 使用select()一次检查fd_setsize套接字,然后遍历所有套接字以接收数据。(对于返回的每个套接字,这不需要两次调用recv吗?msg_peek分配一个缓冲区,然后再次recv()它将与3相同)
    2. 使用select()一次检查一个套接字。(这不是也像3吗?它需要两个调用来接收。)
    3. 将recv()与msg_peek一起使用,一次一个套接字,分配一个缓冲区,然后再次调用recv()。这不是更好吗,因为我们可以跳过所有对select()的调用?还是一个recv()调用的开销太大?

    我已经将情况编码为1和2,但我不确定要使用哪种情况。对不起,如果我有点不清楚的话。

    谢谢

    3 回复  |  直到 14 年前
        1
  •  1
  •   Tony Delroy    14 年前

    …检查时最有效 传入数据(异步)。让我们 假设我有500个连接。我有3个 场景(我可以想到):

    使用select()检查fd_setsize 一次套接字,然后迭代 他们都要接收数据。 (这不需要打两个电话给 是否为每个返回的套接字接收?msg_peek分配一个缓冲区,然后再次recv()它将与3相同)

    我相信你正在仔细构造你的fd集,只使用当前连接的描述符…?然后,对具有读取或异常/错误条件的对象(后者的区别在于BSD和Windows实现)迭代集合并只发出recv()。虽然它在功能上还可以(在概念上也可以说是优雅的),但在大多数实际应用程序中,在接收之前不需要偷看:即使您不确定消息的大小,并且知道可以从缓冲区偷看,也应考虑是否可以:

    • 以块的形式处理消息(例如,读取好的工作单元-可能是8K,处理它,然后将下一个<=8K读取到同一个缓冲区…)
    • 读取到一个足够大的缓冲区中,以容纳大多数/所有消息,并且只有在发现消息不完整时才动态地分配更多的消息。

    使用select()一次检查一个套接字。(这不是也像3吗?它需要两个调用来接收。)

    一点也不好。如果您保持单线程,那么您需要在select上放置一个0超时值,然后像疯了一样在listenig和客户机描述符中旋转。非常浪费CPU时间,会大大降低延迟。

    将recv()与msg_peek一起使用,一次一个套接字,分配一个缓冲区,然后再次调用recv()。这不是更好吗,因为我们可以跳过所有对select()的调用?还是一个recv()调用的开销太大?

    (忽略这一点,最好避免使用msg_peek)-您如何知道msg_peek或recv()所使用的套接字?同样,如果您是单线程的,那么您要么在第一次peek/recv尝试时阻塞,要么使用非阻塞模式,然后疯狂地旋转通过所有描述符,希望peek/recv返回一些东西。浪费的

    所以,坚持1或移动到多线程模型。对于后者,最简单的方法是让监听线程循环调用accept,每次accept都会生成一个新的客户机描述符,它应该生成一个新的线程来处理连接。这些客户端连接处理线程可以简单地在recv()中阻塞。这样,操作系统本身就可以对线程进行监视和唤醒,以响应事件,您可以相信它会相当有效。虽然这个模型听起来很简单,但是您应该知道多线程编程还有很多其他的复杂之处——如果您还不熟悉它,那么您可能不想在了解套接字I/O的同时尝试了解它。

        2
  •  3
  •   Alex Martelli    14 年前

    FD_SETSIZE 通常为1024,因此您可以一次检查所有500个连接。然后,你将表演这两个 recv 电话 只有那些准备好的 --例如,对于一个非常繁忙的系统,每次都有六个。对于其他方法,您还需要大约500个系统调用(大量的“失败”)。 雷科 select 在数百个套接字上执行的调用 随时准备好!-)

    此外,使用方法1,您可以阻止至少一个连接 准备就绪(在这种情况下没有开销,这在不那么繁忙的系统中并不少见)--对于其他方法,您需要进行“轮询”,即, 搅动 ,不断地,烧掉大量的CPU,没有任何好的用途(或者,如果在每次检查循环后你睡一段时间,那么你会延迟响应 尽管 系统一点也不忙——eep!-)

    所以我才考虑 轮询 成为一个 反对的 -模式:经常使用,但仍然具有破坏性。有时你完全没有选择(这基本上告诉你你必须与设计很差的系统交互——唉,有时候在这个不完美的生活中你 不得不!-但是如果有合适的选择 尽管存在,但是进行轮询确实是一个非常糟糕的设计实践,应该避免。

        3
  •  3
  •   YeenFei    14 年前

    您可以简单地对3个场景进行一些效率模拟,其中:

    方案A(0/500输入数据)

    • 对于解决方案1,只能调用single select()
    • 对于解决方案2,您需要500 选择()
    • 对于解决方案3,您需要500 recv()

    方案B(250/500输入数据)

    • 对于解决方案1,单个 选择() +(500) 记录() )
    • 对于解决方案2500 选择() +(500) 记录() )
    • 对于解决方案3750 记录()

    **假设跳过没有缓冲区大小的套接字@no incoming data
    答案显而易见。)

    推荐文章