6

我现在至少阅读了 10 次文档,并且还阅读了大约 10 个代码片段和完整的程序,其中非阻塞套接字用于发送数据。问题是有些教程要么是为初学者准备的(Beejs fi),要么是他们的假设相当草率;那些并不复杂的是专门的代码示例,没有解释他们为什么要做他们所做的事情。send在我看来,即使是 SO 知识库也不能详尽地涵盖整个行为范围。我所追求的是关于 fe 的详细信息:

  • 返回代码 0 究竟表示什么,是否值得检查errno,还是应该放弃连接而不进行进一步调查?
  • 获得负返回值是否保证关闭连接变坏,或者只有这样,除非errnois EWOULDBLOCKEAGAINEINTR(...others) ?
  • errno返回值是否值得检查> 0?显然,该值表示“发送”的数据量(在引号中,因为它确实是一个很长的过程,对),但由于套接字是非阻塞的,这是否意味着可以立即发出另一个调用,或者errno再次取决于,应该等待下一个发送时机(使用 select/poll/epoll)?
  • 基本上,是否首先检查返回值,然后才检查errno值?或者可能在每次调用时send设置,无论如何都返回值?errno这将使错误检查更容易一些......
  • 如果得到EINTR,程序要采取的良好、稳健的行为是什么?只需记录状态并在下一次发送时重试,例如使用EWOULDBLOCKand EAGAIN?
  • 是否同时 检查EWOULDBLOCK EAGAIN?我们可以相信两者具有相同的价值,还是取决于实现?
  • 是否send返回EMSGSIZE流套接字?如果不是,那么缓冲区大小不会太大,对吗?
  • 返回值本身可以等于任何一个已知的错误代码吗?

如果你能提供一个健壮的非阻塞发送代码的例子,那将不胜感激。

4

3 回答 3

9

这里有很多问题:

  • 返回代码 0 究竟表示什么,是否值得检查 errno 还是应该放弃连接而不进行进一步调查?

在 POSIX 系统上,send(2) 永远不会返回 0,除非您使用长度 arg 为 0 调用它。检查特定系统的文档以确保它遵循 POSIX 规范

  • 获得负返回值是否保证关闭连接变坏了,还是只有这样,除非 errno 是 EWOULDBLOCK、EAGAIN 或 EINTR (...others) ?

不,-1 返回值(唯一可能的负返回值)仅表示没有发送数据。您需要检查 errno 以了解 WHY - 请参阅 send(2) 手册页以获取所有可能的 errno 值及其含义的完整列表

  • 返回值 > 0 时是否值得检查 errno ?显然,该值表示“发送”的数据量(在引号中,因为它确实是一个很长的过程,对),但是由于套接字是非阻塞的,这是否意味着可以立即发出另一个调用,或者,再次取决于 errno ,应该等待下一个发送时机(使用select/poll/epoll)?

如果 send 返回成功 (> 0),则 errno 将保持不变,并将包含它之前的任何内容(这可能是一些早期系统调用的错误)。

  • 基本上,是否首先检查返回值,然后才检查 errno 值?或者也许在每次调用时发送设置 errno,不管返回值?这将使错误检查更容易一些......

先检查返回值,如果返回值为-1,再检查errno。如果你真的想,你可以在调用之前将errno设置为0,然后再检查它

  • 如果一个人获得了 EINTR,那么对于一个程序来说,一个好的、健壮的行为会是什么?只需记录状态并在下一次发送时重试,例如 EWOULDBLOCK 和 EAGAIN?

好吧,最简单的方法是禁用系统调用的中断,在这种情况下你永远不会得到 EINTR。像 EWOULDBLOCK/EAGAIN 一样对待它也很好。

  • 是否同时检查 EWOULDBLOCK 和 EAGAIN?我们可以相信两者具有相同的价值,还是取决于实现?

取决于实现,尽管通常它们是相同的。有时 SysV 和 BSD 仿真模式会有些奇怪,这可能会使它们有所不同,并且可能会发生任何一种情况

  • 发送流套接字是否返回 EMSGSIZE?如果不是,那么缓冲区大小不会太大,对吗?

流套接字没有原子消息,EMSGSIZE 仅用于原子消息,所以不,流套接字不能返回 EMSGSIZE

  • 返回值本身可以等于任何一个已知的错误代码吗?

唯一的错误代码是-1。成功是写入的字节数,所以如果你可以在 32 位机器上写入 2^32-1 字节(或在 64 位机器上写入 2^64-1),那将是一个问题,但你不能写那么多字节(如果你尝试,你通常会得到一个 EINVAL 或 EFAULT )。

于 2011-03-20T21:24:23.513 回答
3

我会尽力回答你的问题。

  • 返回值 0 fromsend表示发送了 0 个字节。错误由返回值 -1 指示。如果您调用send的长度为 0,则应该预期返回 0。虽然非阻塞套接字应该返回 -1 并带有 EAGAIN 或 EWOULDBLOCK 的 errno 如果它会阻塞,但如果某些实现返回写入的 0 字节,我不会过分惊讶。
  • EWOULDBLOCK、EAGAIN 和 EINTR 是您应该重试的错误,收到其中之一时不要关闭连接。其他错误确实表明可能会导致关闭的问题。
  • 不,在成功调用库后不要检查 errno(除非文档明确说明您可以出于某种原因执行此操作;我不知道有任何副手这样做)。请注意,errno 在成功的库调用中可能不会保持不变,因为该调用可能进行了其他调用,这些调用返回了预期并正确处理的错误(例如,调用可能会尝试统计一个文件,完全期望它可能不存在;errno即使没有真正的错误,也会是 ENOENT)。如果 send 返回一个简短的写入,您可以再试一次(可能会得到 EWOULDBLOCK/EAGAIN),或者您可以等待下一个select.
  • 是的,首先检查返回值。errno如果调用成功,则不会告诉您任何有用的信息。
  • 在 EINTR 上,您可以立即重试,也可以等待下一次select循环。
  • 您必须同时检查 EAGAIN 和 EWOULDBLOCK;#if EAGAIN == EWOULDBLOCK如果性能特别关键,我想你可以这样做(但请记住,先分析然后优化)。
  • 这一切都取决于底层协议,但通常我希望流协议没有原子消息(除非可能在使用 MSG_OOB 时)。对于 TCP,任何缓冲区大小都可以。
  • 返回值当然有可能等于任何 errno 常量,但这没有任何意义。例如,在我的系统上,如果写入 11 个字节,则返回值将等于 EAGAIN。

HTH。

于 2011-03-20T21:47:21.963 回答
2

EINTR和系统调用上:

  • 如果您使用 GLIBC,则无需担心,至少在系统调用的上下文中。我是从Glibc FAQ中得到的,grep 表示“为什么不再发出信号中断系统调用?”

  • 如果您使用的是 LINUX,那么您可能不必担心 connect() 系统调用的奇怪语义,这是 David Madore 在这里咆哮的。否则,请为异步 connect() 调用的异常行为做好准备。

于 2012-09-03T22:53:06.250 回答