Gao Conghui

好好学习,天天向上

爬虫,视频处理


python随笔--socket关闭之后端口占用的问题

先上一段代码

class MyHttpHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        print "do get"
        self.send_response(code=200)
        self.end_headers()
        self.wfile.write("hello world")

if __name__ == '__main__':
    serv = TCPServer(('', 20001), MyHttpHandler)
    serv.serve_forever()

上面这段代码,运行,client访问多次后关闭,再启动,会报一个socket.error: [Errno 48] Address already in use的错。lsof -i:20001 没能找到任何进程占用端口,一个乍一看很迷的错误,记录下,怕以后忘掉。

解决方案很简单,增加TCPServer.allow_reuse_address = True。具体起作用的为socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)这。

原因如下。操作系统的网络栈会非常谨慎的处理连接的关闭,仅仅用于监听的服务器套接字是可以立即关闭并操作系统忽略的,但是对于实际与客户端进行通信的连接套接字就不行了。即使客户端和服务器都关闭了连接并向对方发从了FIN数据包,连接套接字也无法立即取消。为什么呢?因为即使网络栈发送了最后一个数据包将套接字关闭,也还是无法确认该数据包是否可以被接收。如果数据包正好被网络丢弃了,那么另一方无法得知该数据包长时间无法传达的原因,可能会重新发送FIN数据包,希望能收到响应。

操作系统对上述问题的解决方案为,一个应用程序任务某个TCP连接最终关闭了,操作系统的网络栈实际上会在一个等待状态中将该连接的记录保存最多4分钟。RFC将这些状态命名为CLOSE-WAIT 和TIME-WAIT,当关闭的套接字还处于其中某一状态时,任何最终的FIN数据包都是可以得到适当响应的。

因此,当服务器试图声明某个几分钟前运行的连接所使用的端口时,实际上是在试图声明从某种意义上仍在使用的端口。所以就报错了~

而SO_REUSEADDR可以指明应用程序能够使用一些网络客户端之前的连接正在关闭的端口。

最近的文章

LocalProxy作用的一点感悟

这里的LocalProxy是指 werkzeug.local中的一个类class werkzeug.local.LocalProxy(*local*, *name=None*)[]Acts as a proxy for a werkzeug local. Forwards all operations to a proxied object. The only operations not supported for forwarding are right handed operands...…

python werkzeug继续阅读
更早的文章

mysql随笔之事务隔离级别

这是一个相当基础的问题 SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。 Read Uncommitted(读取未提交内容)在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。 Read Committed(读取提交内容)这是大多数数据库系统的默认...…

mysql随笔继续阅读