我在python中编写了一个简单的多线程游戏服务器,为每个客户端连接创建一个新线程。我发现时不时,服务器会因为管道损坏/ SIGPIPE错误而崩溃。当程序试图将响应发送回不再存在的客户端时,我非常确定它正在发生。
处理这个问题的好方法是什么?我的首选解决方案只是关闭与客户端的服务器端连接并继续,而不是退出整个程序。
PS:This问题/答案以通用方式处理问题;具体应该如何解决?
答案 0 :(得分:51)
假设您正在使用标准套接字模块,您应该捕获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()
进行读取。如果返回空字符串,则远程对等方已关闭连接。因为这里仍然存在竞争条件,所以你仍然需要捕获并处理异常。
扭曲很适合这种事情,但是,听起来你已经编写了相当多的代码。
答案 1 :(得分:37)
阅读try:statement。
try:
# do something
except socket.error, e:
# A socket error
except IOError, e:
if e.errno == errno.EPIPE:
# EPIPE error
else:
# Other error
答案 2 :(得分:3)
SIGPIPE
(虽然我想也许你的意思是EPIPE
?)会出现在套接字上。简单的解决方案是在尝试发送数据之前不要关闭套接字。这也可能发生在管道上,但听起来并不像你正在经历的那样,因为它是一个网络服务器。
您也可以在每个线程的某个顶级处理程序中应用捕获异常的创可贴。
当然,如果您使用Twisted而不是为每个客户端连接生成新线程,那么您可能不会遇到此问题。如果多个线程正在处理相同的I / O通道,那么很难(可能不可能,取决于你的应用程序)获得关闭和写入操作的顺序。
答案 3 :(得分:-1)
我面对同样的问题。但是我下次提交相同的代码,它只是起作用。 第一次破产:
$ packet_write_wait: Connection to 10.. port 22: Broken pipe
第二次运作:
[1] Done nohup python -u add_asc_dec.py > add2.log 2>&1
我想原因可能是当前的服务器环境。
答案 4 :(得分:-3)
我的回答非常接近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获得的错误号。这样您就不会尝试处理权限错误或其他任何您未配备的内容。