在Windows上的同一端口上运行Python Web服务器两次:没有"Port already in use"消息



我在Windows 7上。当我使用以下命令启动瓶子网络服务器时:

run('0.0.0.0', port=80) 

然后再次运行相同的 Python 脚本,它不会Port already in use错误而失败(这应该是正常行为(,而是再次成功启动 Python 脚本!

问题:如何以简单的方式阻止这种行为?

这与侦听同一端口的多个进程有关?,但是如何在Python上下文中防止这种情况?

这是特定于 Windows 的行为,需要在绑定网络套接字之前使用SO_EXCLUSIVEADDRUSE选项。

来自 Windows 套接字 2 文档中的使用SO_REUSEADDR和SO_EXCLUSIVEADDRUSE一文:

在引入SO_EXCLUSIVEADDRUSE套接字选项之前,有 网络应用程序开发人员几乎无法阻止 恶意程序绑定到网络所在的端口 应用程序绑定了自己的套接字。为了解决这个问题 安全问题,Windows套接字引入了SO_EXCLUSIVEADDRUSE 套接字选项,在 Windows NT 4.0 上提供 Service 包 4 (SP4( 及更高版本。

SO_EXCLUSIVEADDRUSE选项是通过调用 setsockopt 来设置的 将 optName 参数设置为 SO_EXCLUSIVEADDRUSE 和 optval 参数在套接字之前设置为 TRUE 的布尔值 绑定。


为了使用 Bottle 模块执行此操作,您必须创建一个自定义后端,以便在绑定套接字之前对其进行访问。这提供了设置所需套接字选项的机会,如文档所示。

这在瓶子部署文档中有简要描述:

如果您喜欢的服务器没有适配器,或者您需要更多适配器 控制服务器设置,您可能需要启动服务器 手动地。


下面是 Bottle Hello World 示例的修改版本,演示了这一点:

import socket
from wsgiref.simple_server import WSGIServer
from bottle import route, run, template
@route('/hello/<name>')
def index(name):
return template('<b>Hello {{name}}</b>!', name=name)
class CustomSocketServer(WSGIServer):
def server_bind(self):
# This tests if the socket option exists (i.e. only on Windows), then
# sets it.
if hasattr(socket, 'SO_EXCLUSIVEADDRUSE'):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
# Everything below this point is a concatenation of the server_bind
# implementations pulled from each class in the class hierarchy.
# wsgiref.WSGIServer -> http.HTTPServer -> socketserver.TCPServer
elif self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
host, port = self.server_address[:2]
self.server_name = socket.getfqdn(host)
self.server_port = port
self.setup_environ()
print "Serving..."
run(host='localhost', port=8080, server_class=CustomSocketServer)   

请注意,需要复制的代码来维护超类的预期行为。

server_bind()的所有超类实现都从调用它们的父类server_bind()开始。这意味着调用其中任何一个都会导致套接字立即绑定,从而消除了设置所需套接字选项的机会。


我使用Python 2.7在Windows 10上对此进行了测试。

一审:

PS C:Userschuckxbottle-test> C:Python27python.exe test.py
Serving...

二审:

PS C:Userschuckxbottle-test> C:Python27python.exe test.py
Traceback (most recent call last):
File "test.py", line 32, in <module>
server_class=CustomSocketServer)
File "C:Python27libwsgirefsimple_server.py", line 151, in make_server
server = server_class((host, port), handler_class)
File "C:Python27libSocketServer.py", line 417, in __init__
self.server_bind()
File "test.py", line 19, in server_bind
self.socket.bind(self.server_address)
File "C:Python27libsocket.py", line 228, in meth
return getattr(self._sock,name)(*args)
socket.error: [Errno 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted

另一种解决方案是使用 @LuisMuñoz 的注释:在再次打开之前检查端口是否已打开:

# Bottle web server code here
# ...
import socket
sock = socket.socket()
sock.settimeout(0.2)  # this prevents a 2 second lag when starting the server
if sock.connect_ex(('127.0.0.1', 80)) == 0:
print "Sorry, port already in use."
exit()
run(host='0.0.0.0', port=80)  

相关内容

最新更新