标签归档:ngrok

如果你在 macOS 上无法访问 Flask 程序

如果你用 macOS 开发 Flask 时无法正常访问程序,或是使用 ngrok 等内网穿透工具时映射的公网地址无法访问,大概率是因为 macOS 新版本(Monterey)的变动导致。你会在页面上看到类似下面的错误信息:

Access to 127.0.0.1 was denied.

You don't have authorization to view this page.

HTTP ERROR 403

或是在执行 flask run 命令时看到类似下面的报错:

OSError: [Errno 48] Address already in use

首先确认你的 macOS 版本是不是 Monterey(左上角 Apple 图标 – About This Mac)或是之后的版本,如果不是那么问题应该和本文无关。

简单来说,新版本的 macOS 上 localhost 5000 端口被一个叫 AirPlay Receiver 的服务占用了。而 Flask 内置服务器默认就运行在 5000 端口,所以会造成端口冲突。当你通过将 host 设为 0.0.0.0 指定 Flask 的内置服务器对外可见,或是使用内网穿透工具时,会发现程序无法访问(有时未设置对外可见也会遇到这个问题)。

最简单的解决方法是关掉这个服务:

  • 系统设置(System Preferences) > 分享(Sharing) > AirPlay Receiver > 取消勾选

或是更改 Flask 开发服务器默认的端口(比如改成 8000)。在执行 flask run 命令时使用 -p/–port 选项可以自定义端口:

$ flask run -p 8000  # 或 flask run --port 8000

或是在执行 flask run 之前通过环境变量 FLASK_RUN_PORT 设置:

$ export FLASK_RUN_PORT=8000 # macOS/Linux
> set FLASK_RUN_PORT=8000 # Windows CMD
> $env:FLASK_RUN_PORT=8000 # Powershell

在 Werkzueg 2.1.0(2022/3/28 发布)版本,如果你执行 flask run –host=0.0.0.0 时检测到了端口被占用,会直接显示相关的错误提示:

$ flask run --host=0.0.0.0
Address already in use
Port 5000 is in use by another program. Either identify and stop that program, or start the server with a different port.
On macOS, try disabling the 'AirPlay Receiver' service from System Preferences -> Sharing.

本文源于去年十一月发的一条推文,最近经常碰见有人遇到这个问题,所以转成这篇文章发出来。

画了一幅涂鸦,送给苹果公司:

使用ngrok让你的本地Flask程序外网可访问

2022/4/3 Update:如果你在 macOS 上映射的 Flask 程序无法访问,可以参考这篇文章解决。

注:本文隶属于《Flask Web开发实战》番外文章系列,文章列表见《Flask Web开发实战番外文章索引》

在开发Web程序时,有时候会有这样的需求:

  • 让朋友可以访问到你本地运行的程序
  • 在本地测试各类服务(比如Telegram机器人、微信公众号等)
  • 有一台废弃的电脑,你想让它作为一台服务器来运行你的博客程序,或是运行其他简单的程序

外网可见

要实现上面的需求,我们就需要程序能够“外网可见”。在《Flask Web开发实战》第一章介绍启动程序时,我们提及了“外网可见”的内容。简单来说,当你将Flask服务器监听的主机地址设置为0.0.0.0时,就可以让服务器外网可见。不过,这里有一个前提,那就是你的服务器需要运行在拥有公网IP的主机上,因为我们开发用的电脑通常不会有公网IP,所以这里的外网只能是你的电脑所在的局域网,比如在客厅的电脑可以访问你的笔记本上运行的程序(通过你的笔记本的内网IP)。

事实上,借助内网穿透/映射工具,我们也可以让外网上的朋友访问到运行在你的笔记本上的程序。这些工具会为我们分配一个域名A,你只需要在本地运行程序,并建立映射,那么当其他用户(不仅是你客厅的电脑,还可能是北京的毛毛,或是美国的Peter)访问A网址时,内网穿透工具就会把请求转发到你的笔记本中,取回响应后再返回给用户。具体流程如下所示:

左边是美国的Peter,右边是你

在这篇文章,我们会了解如何使用ngrok来实现这个目的。本文将会以一个简单的Flask程序作为示例,不过你也可以替换为任意的Web程序,比如使用Django、PHP或是JAVA等语言/框架编写的Web程序。

运行本地服务器

我们先来编写一个简单的Flask程序:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'It works!'

将上面的代码保存为app.py,然后打开一个命令行窗口,使用下面的命令运行这个程序:

$ flask run

如果还没有安装Flask,可以执行pip install flask命令进行安装(你也可以创建一个虚拟环境,推荐使用Pipenv)。

默认情况下,Flask内置的开发服务器会监听本地机的5000端口,你可以使用127.0.0.1:5000或localhost:5000访问程序。

安装和配置ngrok

ngrok支持三大主流操作系统,安装流程比较简单,如下所示:

  1. 访问https://ngrok.com/download
  2. 根据操作系统下载对应的压缩包
  3. 解压到合适的目录

压缩包里包含一个名为ngrok的二进制文件,我们打开一个命令行窗口,切换到这个文件所在的目录。现在可以先执行help命令来测试一下。在Windows下,你可以使用下面的命令:

> .\ngrok help

或是直接执行:

> ngrok help

在Linux/macOS下,则可以使用下面的命令:

$ ./ngrok help

建立映射/HTTP隧道

因为我们的Flask程序已经运行在本地机的5000端口,我们只需要启动ngrok服务,输入对应的端口即可建立映射,或者说建立一条HTTP隧道:

$ ./ngrok http 5000

附注 在Windows下可以使用ngrok http 5000命令。

输出的信息中包含ngrok为你随机分配的域名:

Session Status online
Account Grey Li (Plan: Free)
Version 2.2.8
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://d15a56b1.ngrok.io -> localhost:5000
Forwarding https://d15a56b1.ngrok.io -> localhost:5000

Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00

其中的https://d15a56b1.ngrok.io就是为你分配的可以外部访问的网址,所有发向这个网址的请求都会转发到你的本地机5000端口,即localhost:5000或127.0.0.1:5000。这时访问这个网站会看到我们上面程序中定义的输出“It works!”。

当有请求进入后,你可以在这里看到请求列表。另外,你也可以访问http://127.0.0.1:4040访问ngrok本地程序提供的Web监控页面。

注册账户与付费套餐

在上面的流程里我们并没有介绍注册账户,因为这是可选的。未注册时,你执行ngrok http命令的输出会和上面稍微有些不同,你会看到下面两行:

Session Status online
Session Expires 7 hours, 42 minutes

这是因为未注册账户每个会话只会维持8小时,过期后你需要重新启动。

注册用户没有这个限制,注册相当简单,这里不再赘述,注册完成后需要执行下面的命令连接本地ngrok程序:

$ ./ngrok authtoken <令牌值>

附注 Windows系统可以使用ngrok authtoken <令牌值>命令。

命令中的令牌值可以在注册后跳转的控制面板页面看到,如下图所示:

blank

你也可以访问https://dashboard.ngrok.com/auth查看。

每次建立映射,ngrok都会分配一个随机子域的网址,如果你想拥有一个固定的域名,则要升级套餐。升级套餐的好处很多,但如果只是临时测试用的话,免费账户或是不注册使用就足够了。

注:更多的功能介绍可以访问https://ngrok.com/product查看。文图均来ngrok.com