framework - python web examples
¿Cómo obtener el número de puerto actual en Flask? (5)
El enlace al puerto 0 es correcto. Eso hará que el sistema operativo elija un puerto disponible entre 1024 y 65535 para usted.
Para recuperar el puerto elegido después del enlace, use your_socket.getsockname()[1]
.
Así que todo lo que necesita saber es cómo acceder al zócalo de escucha que usa Flask.
socket.getsockname()
docs: http://docs.python.org/library/socket.html#socket.socket.getsockname
Con Flask , ¿cómo puedo obtener el número de puerto actual al que está conectado el frasco? Quiero iniciar un servidor en un puerto aleatorio usando el puerto 0, pero también necesito saber en qué puerto estoy.
Editar
Creo que he encontrado una solución para mi problema, aunque no es una respuesta a la pregunta. Puedo recorrer los puertos a partir de 49152 e intentar usar ese puerto a través de app.run(port=PORT)
. Puedo hacer esto en un bloque try catch para que, si obtengo un error de Address already in use
, pueda probar el siguiente puerto.
Estoy de acuerdo con la respuesta aceptada de que
No se puede acceder fácilmente al socket del servidor utilizado por Flask, ya que está oculto en la parte interna de la biblioteca estándar (Flask usa Werkzeug, cuyo servidor de desarrollo se basa en la BaseHTTPServer de stdlib).
Sin embargo, acabo de descubrir que Werkzeug expone una opción para pasar un descriptor de archivo para usarlo como socket cuando se ejecuta werkzeug.serving.BaseWSGIServer
(que es lo que la función run()
del matraz termina haciendo). Usando esta funcionalidad, es posible seleccionar un puerto disponible al azar y luego decirle a Werkzeug que lo use.
Aquí está el inicio de mi aplicación:
import logging
import pprint
import sys
from flask import Flask
applog = logging.getLogger(__name__)
applog.setLevel(logging.INFO)
app = Flask(__name__)
if __name__ == ''__main__'':
app.config.from_object(''myapp.default_settings'')
try:
app.config.from_envvar(''MYAPP_SETTINGS'')
except RuntimeError:
applog.warning("MYAPP_SETTINGS environment variable not set or invalid. Using default settings.")
# SERVER_NAME should be in the form of localhost:5000, 127.0.0.1:0, etc...
# So, split on : and take the second element as the port
# If the port is 0, we need to pick a random port and then tell the server to use that socket
if app.config[''SERVER_NAME''] and int(app.config[''SERVER_NAME''].split('':'')[1]) == 0:
import socket, os
# Chose a random available port by binding to port 0
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((app.config[''SERVER_NAME''].split('':'')[0], 0))
sock.listen()
# Tells the underlying WERKZEUG server to use the socket we just created
os.environ[''WERKZEUG_SERVER_FD''] = str(sock.fileno())
# Update the configuration so it matches with the port we just chose
# (sock.getsockname will return the actual port being used, not 0)
app.config[''SERVER_NAME''] = ''%s:%d'' % (sock.getsockname())
# Optionally write the current port to a file on the filesystem
if app.config[''CREATE_PORT_FILE'']:
with open(app.config[''PORT_FILE''], ''w'') as port_file:
port_file.write(app.config[''SERVER_NAME''].split('':'')[1])
applog.info("Running app on %s" % (app.config[''SERVER_NAME'']))
applog.info("Active Settings:%s" % pprint.pformat(app.config, compact=True))
app.run()
Contenido de myapp.default_settings
:
SERVER_NAME = ''127.0.0.1:0''
PORT_FILE = ''current_port''
CREATE_PORT_FILE = True
Lo importante aquí es configurar os.environ[''WERKZEUG_SERVER_FD'']
. Werkzeug observa esta variable de entorno durante la función run_simple
y, si está definida, la pasa como el parámetro fd
a la función make_server
. Esto finalmente se utiliza como el zócalo para la comunicación.
Si bien no puedo garantizar la estabilidad de este enfoque (no sé qué tan bien sea compatible con la variable de entorno WERKZEUG_SERVER_FD
) lo prefiero a las soluciones sugeridas hasta ahora porque:
- No tengo que pasar por un rango de puertos que capturan excepciones, solo obtengo el primer puerto disponible directamente del sistema operativo.
- No hay ninguna posibilidad de que mi puerto aleatorio elegido se tome entre el momento en que vinculo un puerto aleatorio y cuando ejecuto la aplicación porque el puerto que estoy vinculando es el puerto que mi aplicación termina usando.
El código anterior también registra el puerto que se está utilizando y, opcionalmente, escribe el puerto actual que se está utilizando en un archivo especificado por una opción de configuración.
La mayoría de los sistemas operativos en estos días tienen un comando llamado netstat
que le dará una lista de todos los puertos que están actualmente en uso, así como qué aplicaciones se están ejecutando en ellos si lo solicita con la suficiente amabilidad. :)
Debería ser bastante fácil analizar con os.popen
o el módulo de subprocess
.
Aparte de eso, puedes rastrear qué puertos estás usando al iniciar cada servidor ...
Además, si está haciendo esto desde dentro de una solicitud http, puede mirar la variable cgi SERVER_PORT desde el entorno wsgi.
No se puede acceder fácilmente al socket del servidor utilizado por Flask, ya que está oculto en la parte interna de la biblioteca estándar (Flask usa Werkzeug, cuyo servidor de desarrollo se basa en la BaseHTTPServer de BaseHTTPServer
).
Sin embargo, puede crear un puerto efímero y luego cerrar el socket que lo crea, y luego usar ese puerto. Por ejemplo:
# hello.py
from flask import Flask, request
import socket
app = Flask(__name__)
@app.route(''/'')
def hello():
return ''Hello, world! running on %s'' % request.host
if __name__ == ''__main__'':
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((''localhost'', 0))
port = sock.getsockname()[1]
sock.close()
app.run(port=port)
le dará el número de puerto a utilizar. Un ejemplo de ejecución:
$ python hello.py
* Running on http://127.0.0.1:34447/
y, en la navegación a http://localhost:34447/ , veo
¡Hola Mundo! corriendo en localhost: 34447
en mi navegador
Por supuesto, si alguna otra cosa usa ese puerto entre usted para cerrar el zócalo y luego el Flask para abrir el zócalo con ese puerto, recibirá un error de "Dirección en uso", pero es posible que pueda usar esta técnica en su entorno.
Según lo señalado por @VinaySajip Flask usa un socket de servidor estándar pero nunca asigna la instancia a ninguna variable, solo serve_forever()
y llama a serve_forever()
en línea.
De todos modos, en lugar de eso, intente extraer el socket de la aplicación del frasco como dijo @ThiefMaster, podemos interceptar la llamada a bind_socket()
y leer la dirección sin ocuparnos de la creación concurrente de socket. Aquí está mi solución:
from flask import Flask, request
import socketserver
app = Flask("")
original_socket_bind = SocketServer.TCPServer.server_bind
def socket_bind_wrapper(self):
ret = original_socket_bind(self)
print("Socket running at {}:{}".format(*self.socket.getsockname()))
# Recover original implementation
socketserver.TCPServer.server_bind = original_socket_bind
return ret
@app.route("/")
def hello():
return ''Hello, world! running on {}''.format(request.host)
socketserver.TCPServer.server_bind = socket_bind_wrapper #Hook the wrapper
app.run(port=0, debug=True)