【问题标题】:Avoid send to block when not using O_NONBLOCK不使用 O_NONBLOCK 时避免发送阻塞
【发布时间】:2013-04-25 09:42:36
【问题描述】:

我必须为使用 unix 套接字(没有 O_NONBLOCK)的类编写一个聊天客户端-服务器,并在它们上选择异步 I/O。目前在服务端,我从客户端读取1024字节,直接处理。

例如,在有消息的情况下,我会收到一个格式为MSG <msg>的命令(代表客户端发送消息),我会遍历所有已连接客户端的套接字并将消息写入它们。

这种方法确实有效,但我最近通过阅读send 的人发现,如果套接字缓冲区已满并且未在套接字上设置标志 O_NONBLOCK,它会阻塞。

我认为当客户端由于某些原因(崩溃、错误等)未读取时可能会发生此问题,这对我的服务器至关重要,因为它基本上会阻塞,直到该客户端再次读取。

所以这是我的问题:

如果套接字缓冲区已满,在潜在阻塞套接字上避免发送阻塞的正确方法是什么?

我目前仅使用 select 来检查套接字上是否有要读取的内容,但也许我也应该使用它来查看是否也可以在特定套接字上写入?而且,我可以知道选择返回时可以读取/写入多少字节?例如,如果 select “告诉”我可以在这个 socket 上写,我怎么知道我最多可以写多少字节,然后在这个 socket 上写实际上变成阻塞?

谢谢。

【问题讨论】:

  • 您的问题体现了逆向思维。您想要非阻塞行为,但又不想将套接字设置为非阻塞模式。不是一个真正的问题。
  • send() 使用 MSG_DONTWAIT 标志 :) 将它与 select 结合使用是个好主意,因此当缓冲区已满时,您不要只是忙着循环发送。
  • 这正是 O_NONBLOCK 的用途。您没有将描述符设置为 O_NONBLOCK 有什么原因吗?

标签: c sockets unix select


【解决方案1】:

您可以将setsockopt()SO_SNDTIMEO 一起使用来设置send() 将尝试完成其工作的最长时间。

详情请参阅man setsockoptman 7 socket

【讨论】:

    【解决方案2】:

    这可能很可怕。如果您不进入非锁定模式并调用 select(),它会在内部将进程置于睡眠状态以获取特定的超时值。这意味着,fd 将在该特定时间段内被阻止。

    【讨论】:

      【解决方案3】:

      这种方法确实有效,但我最近通过阅读 man of send 发现,如果套接字缓冲区已满并且未在套接字上设置标志 O_NONBLOCK,它会阻塞。

      这就是您使用 select 的原因,但它仍然不可靠,正如 man select 所述:

      在 Linux 下,select() 可能会将套接字文件描述符报告为“准备读取”,但随后的读取会阻塞。例如,当数据有 到达但检查时校验和错误并被丢弃。可能存在文件描述符被虚假报告为就绪的其他情况。因此它可能 在不应阻塞的套接字上使用 O_NONBLOCK 会更安全。

      【讨论】:

        猜你喜欢
        • 2021-09-14
        • 1970-01-01
        • 2022-10-17
        • 1970-01-01
        • 1970-01-01
        • 2012-11-25
        • 2020-10-28
        • 2021-09-24
        • 1970-01-01
        相关资源
        最近更新 更多