有人能给我一个很好的解释非阻塞套接字的'发送'行为吗?

时间:2011-03-20 20:57:27

标签: sockets send nonblocking berkeley-sockets

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

  • 返回代码0的确切含义是什么,是否值得检查errno或者是否应该在没有进一步调查的情况下丢弃连接?
  • 获得负回报值是否可以保证关闭连接变得糟糕,或者仅在errnoEWOULDBLOCKEAGAINEINTR(...其他)?
  • 当返回值为errno时,是否值得检查> 0?显然,该值表示“已发送”的数据量(在引号中,因为它确实是一个很长的过程,正确),但由于套接字是非阻塞的,这是否意味着可以立即发出另一个调用,或者,取决于{{ 1}}再次,应该等待下一个发送时机(使用select / poll / epoll)?
  • 基本上,首先检查返回值,然后检查errno值吗?或者也许errno在每次通话时设置send,无论如何返回值?这样可以使错误检查更容易......
  • 如果有人获得errno,那么程序要采取什么样的良好,强健的行为?只需记录状态并在下次发送时重试,例如EINTREWOULDBLOCK
  • 是否检查 EAGAIN EWOULDBLOCK?我们可以信任两者具有相同的价值,还是依赖于实施?
  • EAGAIN是否会为流套接字返回send?如果没有,那么没有缓冲区大小太大,对吗?
  • 返回值本身是否等于任何已知的错误代码?

如果你能提供一个强大的非阻塞发送代码的例子,那将是绝对赞赏的。

3 个答案:

答案 0 :(得分:9)

这里有很多问题:

  
      
  • 返回代码0的确切含义是什么,是否值得检查errno或者是否应该在没有进一步调查的情况下丢弃连接?
  •   

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

  
      
  • 获得负回报值是否可以保证关闭连接变坏,或者只是如此,除非errno是EWOULDBLOCK,EAGAIN或EINTR(......其他)?
  •   

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

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

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

  
      
  • 基本上,首先检查返回值,然后再检查errno值吗?或者也许在每次通话时发送套装errno,无论如何返回值?这样可以使错误检查更容易......
  •   

首先检查返回值,然后检查错误,如果返回值为-1。如果你真的想,你可以在通话前将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)。

答案 1 :(得分:3)

我会尽力回答你的问题。

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

HTH。

答案 2 :(得分:2)

EINTR 和系统调用上:

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

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