1

如何使 TCP 服务器正确关闭套接字?

我编写了一个简单的 TCP 服务器来将一些信息回显给客户端。现在我想在 localhost:8088 上使用它,killall并在我处理代码时随时停止使用并重新启动。

但是,我无法让它关闭所有套接字并“释放”地址,所以当我快速进行一些测试时,修复代码中的某些内容,然后停止 (Ctrl+C) 并再次启动服务器,我得到socket.error: [Errno 98] Address already in use.

当 I 时sudo netstat -ntap,我仍然可以看到几个127.0.0.1:8088处于 TIME_WAIT 状态的套接字。所以我必须等到他们“消亡”。

我的测试用例:

#!/usr/bin/python
import SocketServer

class MyEchoHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        self.data = self.request.recv(1024).strip()
        # do something smart with the data, but for now, just say hello.
        self.reply = "Content-Type: text/plain\r\n\r\nhello."
        self.request.send(self.reply)
        self.request.close()

def main():
    server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler)
    server.serve_forever()

if __name__ == '__main__':
    HOST, PORT = "localhost", 8088
    main()

我究竟做错了什么?应该self.request.close()不够吧?

我正在使用 Python 2.7.3 在 Debian 上尝试这个,尽管我也需要使用 Python 2.6 支持 Squeeze。

4

2 回答 2

5

TCP 堆栈将端口置于定时等待一段时间(取决于您的系统,大约需要 30 秒到数分钟)。您可以使用 SO_REUSEADDR 套接字选项更改该行为。诀窍是必须在绑定端口之前设置该选项。

如果你有原始套接字,你可以:

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

SocketServer 允许您使用 allow_reuse_address 属性全局设置选项:

SocketServer.TCPServer.allow_reuse_address = True

或者您可以在创建服务器时执行此操作:

def main():
    server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler, False)
    server.allow_reuse_address = True
    server.server_bind()
    server.serve_forever()

甚至重写绑定方法:

class MyTCPServer(SocketServer.TCPServer):

    def server_bind(self):
        self.allow_reuse_address = True
        super(MyTCPServer, self).server_bind()

以下是在我的 Windows 机器上为我工作的 3 个解决方案。

(1) 全局设置

#!/usr/bin/python
import SocketServer
import time

SocketServer.allow_reuse_address = True

class MyEchoHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        self.data = self.request.recv(1024).strip()
        # do something smart with the data, but for now, just say hello.
        self.reply = "Content-Type: text/plain\r\n\r\n" + time.asctime()
        self.request.send(self.reply)
        self.request.close()

def main():
    server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler)
    server.serve_forever()

if __name__ == '__main__':
    HOST, PORT = "localhost", 8088
    main()

(2) 本地设置

#!/usr/bin/python
import SocketServer
import time

class MyEchoHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        self.data = self.request.recv(1024).strip()
        # do something smart with the data, but for now, just say hello.
        self.reply = "Content-Type: text/plain\r\n\r\n" + time.asctime()
        self.request.send(self.reply)
        self.request.close()

def main():
    server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler, False)
    server.allow_reuse_address = True
    server.server_bind()
    server.server_activate()
    server.serve_forever()

if __name__ == '__main__':
    HOST, PORT = "localhost", 8088
    main()

(3) 继承

#!/usr/bin/python
import SocketServer
import time

class MyEchoHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        self.data = self.request.recv(1024).strip()
        # do something smart with the data, but for now, just say hello.
        self.reply = "Content-Type: text/plain\r\n\r\n" + time.asctime()
        self.request.send(self.reply)
        self.request.close()

class MyTCPServer(SocketServer.TCPServer):

    def server_bind(self):
        self.allow_reuse_address = True
        SocketServer.TCPServer.server_bind(self)

def main():
    server = MyTCPServer((HOST, PORT), MyEchoHandler)
    server.serve_forever()

if __name__ == '__main__':
    HOST, PORT = "localhost", 8088
    main()
于 2013-07-15T19:01:02.490 回答
-1

您可以调用 server.shutdown() 但您必须从另一个线程 8-( IMO 这是 Python TCPServer 中的薄弱环节。

于 2013-07-15T16:56:43.483 回答