如何让TCP服务器正确关闭套接字?
我写了一个简单的TCP服务器来回复一些信息回客户端。现在我想在localhost:8088上使用它并使用killall
停止它,并在我处理代码时随时重启。
但是,我无法关闭所有套接字并“释放”地址,因此当我快速进行一些测试时,修复代码中的内容然后停止(Ctrl + C)并再次启动服务器,我得到socket.error: [Errno 98] Address already in use
。
当我sudo netstat -ntap
时,我仍然可以看到TIME_WAIT状态下的127.0.0.1:8088
个套接字很少。所以我必须等到他们“消亡”。
我的测试用例:
#!/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。
答案 0 :(得分:4)
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()
答案 1 :(得分:-1)
你可以调用server.shutdown(),但你必须从另一个线程8-(IMO这是Python的TCPServer中的弱区域)。