在Windows的同一端口上两次运行Python Web服务器:没有“端口已在使用”消息

时间:2018-06-28 20:17:16

标签: python windows networking windows-7 bottle

我在Windows 7上。当我使用以下命令启动Bottle Web服务器时:

run('0.0.0.0', port=80) 

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

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

这与Multiple processes listening on the same port?有关,但是如何在Python上下文中防止这种情况?

2 个答案:

答案 0 :(得分:2)

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

来自Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE article in the Windows Socket 2 documentation

  

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

     

...

     

通过调用setsockopt设置SO_EXCLUSIVEADDRUSE选项   optname参数设置为SO_EXCLUSIVEADDRUSE的函数,并且   套接字之前,optval参数设置为布尔值TRUE   绑定。


为了使用Bottle模块执行此操作,您必须创建一个自定义后端,以方便在绑定之前访问套接字。这样可以设置所需的套接字选项,如所记录的那样。

Bottle Deployment documentation中对此进行了简要说明:

  

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


这是Bottle Hello World example的修改版本,证明了这一点:

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

答案 1 :(得分:-1)

另一种解决方案是在再次打开之前使用@LuisMuñoz的注释:check if the port is already opened

# 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)