Запуск дважды веб-сервера Python на одном и том же порту в Windows: сообщение «Порт уже используется» отсутствует - PullRequest
0 голосов
/ 28 июня 2018

Я на Windows 7. Когда я запускаю веб-сервер Bottle с:

run('0.0.0.0', port=80) 

И затем еще раз запускает тот же скрипт Python, он не завершается с ошибкой Port already in use (это должно быть нормальным поведением), но вместо этого успешно запускает Python Сценарий снова!

Вопрос: Как остановить это поведение простым способом?

Это связано с Несколько процессов, прослушивающих один и тот же порт? , но как вы можете предотвратить это в контексте Python?

Ответы [ 2 ]

0 голосов
/ 29 июня 2018

Альтернативное решение - использовать комментарий @ 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)  
0 голосов
/ 29 июня 2018

Это специфическое поведение 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
...