当你从它抛出异常时,python对象会发生什么

时间:2009-09-25 02:49:52

标签: python exception garbage-collection sockets

我的类包含一个连接到服务器的套接字。该类的一些方法可以抛出异常。我正在运行的脚本包含一个外部循环,它捕获异常,记录错误,并创建一个尝试重新连接到服务器的新类实例。

问题是服务器一次只处理一个连接(按设计),并且“旧”套接字仍然连接。因此,新的连接尝试会挂起脚本。我可以通过强制关闭旧套接字来解决这个问题,但我想知道:为什么套接字不会自动关闭?

当它“卡住”时,netstat显示连接到端口的两个插槽。服务器正在等待来自第一个套接字的输入,但它还没有处理新的。

我针对虚拟服务器运行此操作,该服务器向每个传入行回复“error \ n”。

编辑:请参阅我对Mark Rushakoff's answer below的评论。在异常处理程序中的断言(False)[我随后捕获]似乎强制套接字关闭。

import socket

class MyException(Exception):
    pass

class MyClient(object):

    def __init__(self, port):
        self.sock = socket.create_connection(('localhost', port))
        self.sockfile = self.sock.makefile()

    def do_stuff(self):
        self._send("do_stuff\n")
        response = self._receive()
        if response != "ok\n":
            raise MyException()
        return response

    def _send(self, cmd):
        self.sockfile.write(cmd)
        self.sockfile.flush()

    def _receive(self):
        return self.sockfile.readline()

def connect():
    c = MyClient(9989)
    # On the second iteration, do_stuff() tries to send data and
    # hangs indefinitely.
    print c.do_stuff()

if __name__ == '__main__':
    for _ in xrange(3):
        try:
            connect()
        except MyException, e:
            print 'Caught:', e
            # This would be the workaround if I had access to the
            # MyClient object:
            #c.sock.close()
            #c.sockfile.close()

编辑:这是(丑陋的)服务器代码:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
s.bind(('localhost', 9989))
s.listen(5)
(c,a) = s.accept()
f = c.makefile()
print f.readline()
f.write('error\n')
f.flush()
(c2,a) = s.accept()
f = c.makefile()
print f.readline()
s.close()

4 个答案:

答案 0 :(得分:5)

这是垃圾收集的工件。即使对象超出范围,也不一定收集,因此销毁直到垃圾收集运行发生 - 这不像C ++,一旦对象失去范围就会调用析构函数。

您可以通过将connect更改为

来解决此特定问题
def connect():
    try:
        c = MyClient(9989)
        # On the second iteration, do_stuff() tries to send data and
        # hangs indefinitely.
        print c.do_stuff()
    finally:
        c.sock.close()
        c.sockfile.close()

或者,您可以为__enter__定义__exit__MyClient,然后执行a with statement

def connect():
    with MyClient(9989) as c:
        print c.do_stuff()

这与try-finally实际上是一样的。

答案 1 :(得分:1)

你能真正处理connect()中的异常吗?

我认为您应该提供MyClient.close()方法,并像这样编写connect():

def connect():
    try:
        c = MyClient(9989)
        print c.do_stuff()
    finally:
        c.close()

这与类似文件的对象(以及with语句)完全类比

答案 2 :(得分:0)

好的,这是最终版本。当事情变得僵硬时,明确地关闭套接字对象。

import socket

class MyException(Exception):
    pass

class MyClient(object):

    def __init__(self, port):
        self.sock = socket.create_connection(('localhost', port))
        self.sockfile = self.sock.makefile()

    def close(self):
        self.sock.close()
        self.sockfile.close()

    def do_stuff(self):
        self._send("do_stuff\n")
        response = self._receive()
        if response != "ok\n":
            raise MyException()
        return response

    def _send(self, cmd):
        self.sockfile.write(cmd)
        self.sockfile.flush()

    def _receive(self):
        return self.sockfile.readline()

def connect():
    try:
        c = MyClient(9989)
        print c.do_stuff()
    except MyException:
        print 'Caught MyException'
    finally:
        c.close()

if __name__ == '__main__':
    for _ in xrange(2):
        connect()

答案 3 :(得分:0)

可以通过将相关对象设置为None来标记垃圾收集器以进行清理。