我用 python 编写了一个简单的多线程游戏服务器,它为每个客户端连接创建一个新线程。我发现,由于管道/SIGPIPE 错误,服务器会时不时地崩溃。我很确定当程序尝试将响应发送回不再存在的客户端时会发生这种情况。
有什么好的方法来解决这个问题?我首选的解决方案是简单地关闭与客户端的服务器端连接并继续前进,而不是退出整个程序。
PS:这个问题/答案以通用的方式处理问题;我应该如何具体解决它?
我用 python 编写了一个简单的多线程游戏服务器,它为每个客户端连接创建一个新线程。我发现,由于管道/SIGPIPE 错误,服务器会时不时地崩溃。我很确定当程序尝试将响应发送回不再存在的客户端时会发生这种情况。
有什么好的方法来解决这个问题?我首选的解决方案是简单地关闭与客户端的服务器端连接并继续前进,而不是退出整个程序。
PS:这个问题/答案以通用的方式处理问题;我应该如何具体解决它?
假设您使用的是标准套接字模块,您应该捕获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 非常适合这类事情,但是,听起来您已经编写了相当多的代码。
阅读 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
?)在您关闭套接字然后向其发送数据时发生在套接字上。简单的解决方案是在尝试发送数据之前不要关闭套接字。这也可能发生在管道上,但听起来不像你正在经历的那样,因为它是一个网络服务器。
您也可以在每个线程的某些顶级处理程序中应用捕获异常的创可贴。
当然,如果您使用Twisted而不是为每个客户端连接生成一个新线程,您可能不会遇到这个问题。如果多个线程正在处理相同的 I/O 通道,那么很难(可能不可能,具体取决于您的应用程序)获得正确的关闭和写入操作的顺序。
我面临同样的问题。但是我下次提交相同的代码,它就可以了。第一次坏了:
$ packet_write_wait: Connection to 10.. port 22: Broken pipe
第二次起作用:
[1] Done nohup python -u add_asc_dec.py > add2.log 2>&1
我猜原因可能与当前的服务器环境有关。
我的答案非常接近 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 获得的错误编号。这样您就不会尝试处理权限错误或其他任何您不具备的条件。