如何在python中处理损坏的管道(SIGPIPE)?

如何在python中处理损坏的管道(SIGPIPE)?,python,python-2.x,broken-pipe,Python,Python 2.x,Broken Pipe,我用python编写了一个简单的多线程游戏服务器,为每个客户端连接创建一个新线程。我发现每隔一段时间,服务器都会因为管道破裂/SIGPIPE错误而崩溃。我敢肯定,当程序试图将响应发送回不再存在的客户端时,就会发生这种情况 处理这个问题的好方法是什么?我的首选解决方案是简单地关闭与客户端的服务器端连接并继续,而不是退出整个程序 PS:问题/答案以一般方式处理问题;我应该如何具体解决它?仔细阅读try:语句 try: # do something except socket.error, e

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

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


PS:问题/答案以一般方式处理问题;我应该如何具体解决它?

仔细阅读try:语句

try:
    # do something
except socket.error, e:
    # A socket error
except IOError, e:
    if e.errno == errno.EPIPE:
        # EPIPE error
    else:
        # Other error
SIGPIPE
(尽管我想你的意思可能是
EPIPE
?)发生在套接字上,当你关闭一个套接字然后向它发送数据时。简单的解决方案是在尝试发送数据之前不要关闭套接字。这也可能发生在管道上,但听起来不像是您正在经历的,因为它是一个网络服务器

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


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

我的答案与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获得的错误号。这样,您就不会试图处理权限错误或任何其他您没有配备的错误。

假设您使用的是标准套接字模块,您应该捕获
socket.error:(32,'断管')
异常(而不是其他人建议的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
请注意,在对闭合套接字的第一次写入时(通常是在第二次写入时)(除非第一次写入中写入的字节数大于套接字的缓冲区大小),不会总是引发此异常。您需要记住这一点,以防您的应用程序认为远程端在可能已断开连接的情况下从第一次写入中接收到数据

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非常适合这种类型的东西,但是,听起来您已经编写了相当多的代码。

我面临同样的问题。但我下次提交相同的代码时,它只是工作。 它第一次破裂时:

$ packet_write_wait: Connection to 10.. port 22: Broken pipe
第二次工作时:

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

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

这是一个糟糕的策略。但是,它会抓住任何一种例外。你知道这是个错误。处理这件事。如果出现其他情况,找出原因并妥善处理。您不希望屏蔽诸如被零除或内存不足之类的错误。如果您使用Python的套接字模块,则不会得到IOError异常:您将得到一个socket.error异常。对于断开的管道套接字异常,您不会得到errno==EPIPE的IOError,您将得到socket.error,因此,在IOError异常处理程序中检查它没有意义。你有2票赞成(仍然)不好的答案。也许你应该投票支持我的答案:)同意提问者现在应该有一些关于该做什么的线索。您的代码片段仍然不正确,提问者可能会遵循您的示例。这不会破坏他的代码,只是在IOError处理程序中检查EPIPE是无用的。@mhawke:你仍然是对的。两次都是。但是,很难设计一个标准操作系统错误(带有errno)和其他错误(没有errno)的示例。我认为有一个整洁的代码示例很重要——我不是为他们编写应用程序的。错误号应该是32,而不是23。我应该澄清一下,我的意思是“23”作为占位符。真正地32? 我比我想象的要近。:-)简单的解决方案是在尝试发送数据之前不要关闭套接字。在这里,您假设套接字在本地(服务器端)关闭,而在回答中,我们看到当您写入另一(客户端)端完全关闭的套接字时,通常会发生这种情况。你是故意省略这个案例还是不同意这个说法?如果是实例(例如args,tuple):,这看起来很奇怪。有人能解释一下吗?这意味着,“e.args是一个元组吗?”为什么EPIPE不总是在第一次写入时出现?EPIPE升起的条件是什么?