<p>这是Windows特有的行为,需要在绑定网络套接字之前使用<code>SO_EXCLUSIVEADDRUSE</code>选项。在</p>
<p>从<a href="https://docs.microsoft.com/en-us/windows/desktop/WinSock/using-so-reuseaddr-and-so-exclusiveaddruse" rel="nofollow noreferrer"><strong>Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE</strong> article in the Windows Socket 2 documentation</a>:</p>
<blockquote>
<p>Before the SO_EXCLUSIVEADDRUSE socket option was introduced, there was
very little a network application developer could do to prevent a
malicious program from binding to the port on which the network
application had its own sockets bound. In order to address this
security issue, Windows Sockets introduced the SO_EXCLUSIVEADDRUSE
socket option, which became available on Windows NT 4.0 with Service
Pack 4 (SP4) and later.</p>
<p>...</p>
<p>The SO_EXCLUSIVEADDRUSE option is set by calling the setsockopt
function with the optname parameter set to SO_EXCLUSIVEADDRUSE and the
optval parameter set to a boolean value of TRUE before the socket is
bound.</p>
</blockquote>
<hr/>
<p>为了使用Bottle模块来实现这一点,您必须创建一个定制的后端,以便在绑定套接字之前访问它。这提供了一个按文档设置所需套接字选项的机会。在</p>
<p>这在<a href="http://bottlepy.org/docs/dev/deployment.html#switching-the-server-backend" rel="nofollow noreferrer">Bottle Deployment documentation</a>中有简要描述:</p>
<blockquote>
<p>If there is no adapter for your favorite server or if you need more
control over the server setup, you may want to start the server
manually.</p>
</blockquote>
<hr/>
<p>以下是<a href="https://github.com/bottlepy/bottle#example-hello-world-in-a-bottle" rel="nofollow noreferrer">Bottle Hello World example</a>的修改版本,它演示了这一点:</p>
<pre><code>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)
</code></pre>
<p>请注意,复制的代码是维护超级类的预期行为所必需的。在</p>
<p><code>server_bind()</code>的所有超级类实现都是从调用其父类<code>server_bind()</code>开始的。这意味着调用它们中的任何一个都会导致立即绑定套接字,从而消除了设置所需套接字选项的机会。在</p>
<hr/>
<p>我用Python2.7在Windows10上测试了这一点。在</p>
<p>初审:</p>
^{pr2}$
<p>二审:</p>
<pre><code>PS C:\Users\chuckx\bottle-test> C:\Python27\python.exe test.py
Traceback (most recent call last):
File "test.py", line 32, in <module>
server_class=CustomSocketServer)
File "C:\Python27\lib\wsgiref\simple_server.py", line 151, in make_server
server = server_class((host, port), handler_class)
File "C:\Python27\lib\SocketServer.py", line 417, in __init__
self.server_bind()
File "test.py", line 19, in server_bind
self.socket.bind(self.server_address)
File "C:\Python27\lib\socket.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
</code></pre>