如何在Python失败后关闭socket?

-1 投票
3 回答
4135 浏览
提问于 2025-04-18 10:11

首先,我在使用Python时遇到了套接字(sockets),并且碰到了一个问题:当我的Python代码出现错误时,比如在第二个脚本启动时,conn.close()之前有语法错误,导致端口被占用。虽然脚本已经结束,但套接字仍然是打开的,感觉就像是一个忙碌的套接字。

这里有一个错误的例子:

web@web-X501A1 /var/www $ cd /home/web/www/public/py
web@web-X501A1 ~/www/public/py $ python sockets.py
connected: ('127.0.0.1', 47168)
Traceback (most recent call last):
  File "sockets.py", line 164, in <module>
    data = re.find('(<onvif>.*<\/onvif>)')
AttributeError: 'module' object has no attribute 'find'
web@web-X501A1 ~/www/public/py $ python sockets.py
Traceback (most recent call last):
  File "sockets.py", line 154, in <module>
    sock.bind(('', 9090))
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 98] Address already in use
web@web-X501A1 ~/www/public/py $ python sockets.py
Traceback (most recent call last):
  File "sockets.py", line 154, in <module>
    sock.bind(('', 9090))
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 98] Address already in use

代码:

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', 9090))
sock.listen(1)
conn, addr = sock.accept()

try:
    print 'connected:', addr

    buffer = ''

    while True:
        buffer += conn.recv(1024)
        data = re.find('(<code>.*<\/code>)', buffer)
        print data
        exit();
        if not data:
            continue
        conn.send(data.upper())
except Exception:
    pass
finally:
    conn.close()

3 个回答

0

如果你使用 netstat -nutap 命令,你会发现你的连接状态显示为 TIME_WAIT,看起来好像还在运行。

这其实是 TCP 协议的一部分。根据 维基百科 的解释:

这个状态表示正在等待足够的时间,以确保远程的 TCP 收到了它的连接终止请求的确认。根据 RFC 793 的规定,一个连接可以在 TIME-WAIT 状态下保持最长四分钟,这个时间被称为 MSL(最大报文生存时间)。

所以,当你试图立即重新连接到同一个端口时,Python 会抱怨说这个端口仍然在使用中,无法绑定,提示信息是:

socket.error: [Errno 98] Address already in use

可以参考 这个 老问题,里面讨论了如何避免这个等待时间。

2

在使用套接字的时候,把它放在一个try/finally的结构里。在finally部分关闭这个套接字。你也可以在except部分处理可能出现的错误。大概可以这样写:

try:
    result = x / y
except ZeroDivisionError:
    print "division by zero!"
else:
    print "result is", result
finally:
    print "executing finally clause"
1

这里的问题是,当脚本崩溃时,连接没有按照正常的步骤关闭,这就导致了“脏”套接字关闭。幸运的是,有一个简单的解决办法,可以告诉操作系统忽略这个套接字已经在使用中的事实(也就是它绑定的端口):

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

就这么简单,把这个代码加在绑定调用之前就可以了。完成这个后,调试其他错误会变得更简单,也省时多了哦 ;) 想了解更多,可以查看文档 https://docs.python.org/2/library/socket.html#socket.socket.setsockopt

撰写回答