【问题标题】:How to handle a broken pipe (SIGPIPE) in python?如何在 python 中处理损坏的管道(SIGPIPE)?
【发布时间】:2010-09-15 20:34:52
【问题描述】:

我用 python 编写了一个简单的多线程游戏服务器,它为每个客户端连接创建一个新线程。我发现服务器会时不时地因为管道/SIGPIPE 错误而崩溃。我很确定当程序尝试将响应发送回不再存在的客户端时会发生这种情况。

有什么好的方法来解决这个问题?我首选的解决方案是简单地关闭与客户端的服务器端连接并继续前进,而不是退出整个程序。

PS:This question/answer 以通用的方式处理问题;具体应该怎么解决?

【问题讨论】:

    标签: python python-2.x broken-pipe


    【解决方案1】:

    我面临同样的问题。但是我下次提交相同的代码,它就可以了。 第一次坏了:

    $ packet_write_wait: Connection to 10.. port 22: Broken pipe
    

    第二次生效:

    [1]   Done                    nohup python -u add_asc_dec.py > add2.log 2>&1
    

    我猜原因可能与当前服务器环境有关。

    【讨论】:

      【解决方案2】:

      阅读 try: 语句。

      try:
          # do something
      except socket.error, e:
          # A socket error
      except IOError, e:
          if e.errno == errno.EPIPE:
              # EPIPE error
          else:
              # Other error
      

      【讨论】:

      • 除此之外的毯子是一个糟糕的政策。但是,它会捕获任何类型的异常。你知道这是一个 IOError。处理那个。如果出现其他问题,请找出原因并妥善处理。您不想掩盖除零或内存不足等错误。
      • 如果你使用 Python 的 socket 模块,你不会得到一个 IOError 异常:你会得到一个 socket.error 异常。
      • 你不会得到 errno == EPIPE 的 IOError 用于损坏的管道套接字异常,你会得到 socket.error,所以在 IOError 异常处理程序中检查它是没有意义的。您对(仍然)不好的答案有 2 票。也许你应该投票给我的答案:)
      • 同意提问者现在应该知道该怎么做。不过,您的代码 sn-p 仍然不正确,提问者可能会效仿您的示例。它不会破坏他的代码,只是在 IOError 处理程序中检查 EPIPE 是没用的。
      • @mhawke:你还是对的。两次。但是,很难设计出标准操作系统错误(带 errno)和其他错误(不带 errno)的示例。我认为有一个整洁的代码示例很重要——我不是为他们编写他们的应用程序。
      【解决方案3】:

      假设您使用的是标准套接字模块,您应该捕获socket.error: (32, 'Broken pipe') 异常(不是其他人建议的 IOError)。这将在您描述的情况下引发,即发送/写入远程端已断开连接的套接字。

      import socket, errno, time
      
      # setup socket to listen for incoming connections
      s = socket.socket()
      s.bind(('localhost', 1234))
      s.listen(1)
      remote, address = s.accept()
      
      print "Got connection from: ", address
      
      while 1:
          try:
              remote.send("message to peer\n")
              time.sleep(1)
          except socket.error, e:
              if isinstance(e.args, tuple):
                  print "errno is %d" % e[0]
                  if e[0] == errno.EPIPE:
                     # remote peer disconnected
                     print "Detected remote disconnect"
                  else:
                     # determine and handle different error
                     pass
              else:
                  print "socket error ", e
              remote.close()
              break
          except IOError, e:
              # Hmmm, Can IOError actually be raised by the socket module?
              print "Got IOError: ", e
              break
      

      请注意,第一次写入已关闭的套接字时并不总是会引发此异常 - 更常见的是第二次写入(除非第一次写入时写入的字节数大于套接字的缓冲区大小)。您需要记住这一点,以防您的应用程序认为远程端在可能已经断开连接时收到了第一次写入的数据。

      您可以使用select.select()(或poll)减少这种情况的发生(但不能完全消除)。在尝试写入之前检查准备好从对等方读取的数据。如果select 报告有可从对等套接字读取的数据,则使用socket.recv() 读取它。如果这返回一个空字符串,则远程对等方已关闭连接。因为这里仍然存在竞争条件,所以您仍然需要捕获并处理异常。

      Twisted 非常适合这类事情,但是,听起来您已经编写了相当多的代码。

      【讨论】:

      • 这看起来很奇怪if isinstance(e.args, tuple):。有人可以解释一下吗?
      • 也就是说,“e.args 是一个元组吗?”
      • 为什么 EPIPE 并不总是在第一次写入时引发? EPIPE的产生条件是什么?
      【解决方案4】:

      我的答案与 S.Lott 的答案非常接近,但我会更加具体:

      try:
          # do something
      except IOError, e:
          # ooops, check the attributes of e to see precisely what happened.
          if e.errno != 23:
              # I don't know how to handle this
              raise
      

      其中“23”是您从 EPIPE 获得的错误编号。这样您就不会尝试处理权限错误或其他任何您不具备的能力。

      【讨论】:

      • errno 应该是 32,而不是 23。
      • 我应该澄清我的意思是“23”作为占位符。真的吗? 32?我比我想象的更接近。 :-)
      【解决方案5】:

      SIGPIPE(虽然我认为您的意思可能是EPIPE?)当您关闭套接字然后向其发送数据时,会在套接字上发生。简单的解决方案是在尝试发送数据之前不要关闭套接字。这也可能发生在管道上,但听起来不像你正在经历的那样,因为它是一个网络服务器。

      您也可以在每个线程的某些顶级处理程序中应用捕获异常的创可贴。

      当然,如果您使用Twisted 而不是为每个客户端连接生成一个新线程,您可能不会遇到这个问题。如果多个线程正在处理同一个 I/O 通道,那么很难(可能是不可能的,具体取决于您的应用程序)使关闭和写入操作的顺序正确。

      【讨论】:

      • 简单的解决方案是在尝试发送数据之前不要关闭套接字。您在这里假设套接字已在本地(在服务器端)关闭,而在this 回答我们读到这通常发生在您写入在另一端(客户端)完全关闭的套接字时。您是否故意省略了这种情况,或者您不同意这种说法?
      猜你喜欢
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      • 2011-11-04
      • 1970-01-01
      • 2010-10-28
      • 2011-03-19
      • 1970-01-01
      相关资源
      最近更新 更多