Это специфическое поведение Windows, которое требует использования опции SO_EXCLUSIVEADDRUSE
перед привязкой сетевого сокета.
Из статьи Использование SO_REUSEADDR и SO_EXCLUSIVEADDRUSE в документации Windows Socket 2 :
До того, как была добавлена опция сокета SO_EXCLUSIVEADDRUSE,
очень мало разработчик сетевых приложений может сделать, чтобы предотвратить
вредоносная программа от привязки к порту, по которому работает сеть
Приложение имеет свои собственные связанные сокеты. Для решения этой проблемы
проблема безопасности, Windows Sockets представила SO_EXCLUSIVEADDRUSE
опция сокета, которая стала доступной в Windows NT 4.0 со службой
Пакет 4 (SP4) и позже.
...
Параметр SO_EXCLUSIVEADDRUSE устанавливается путем вызова setsockopt
функция с параметром optname, установленным в SO_EXCLUSIVEADDRUSE, и
Для параметра optval установлено булево значение TRUE до того, как сокет
оценка.
Чтобы сделать это с помощью модуля Bottle, вы должны создать пользовательский бэкэнд, облегчающий доступ к сокету до его привязки. Это дает возможность установить требуемый параметр сокета, как описано в документации.
Это кратко описано в документации по развертыванию бутылок :
Если нет адаптера для вашего любимого сервера или вам нужно больше
контроль над настройкой сервера, вы можете запустить сервер
вручную.
Вот модифицированная версия примера Hello World * Bottle, демонстрирующая это:
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()
. Это означает, что вызов любого из них приводит к немедленной привязке сокета, что исключает возможность установить требуемый параметр сокета.
Я проверял это на Windows 10 с использованием Python 2.7.
Первый экземпляр:
PS C:\Users\chuckx\bottle-test> C:\Python27\python.exe test.py
Serving...
Второй экземпляр:
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