手机连上WiFi后自动弹出页面原理探究及使用8266实现

Captive Portal for ESP


强制网络门户(captive portal)就是手机在连接上某个wifi的时候,自动弹出一个页面,这个页面通常是要我们输入账号密码才能连接网络。前几年wifi还没有大面积普及的时候,在商场或餐厅提供的wifi常常使用这个功能。现在wifi设备多了,这个功能逐渐被遗忘了,但是在物联网方面仍然有一定的用处,比如手机连上wifi自动后弹出配网页面。

 

原理


手机连上wifi后,但不能保证能连接到互联网,所以手机连上某个wifi的第一件事就是检查下这个wifi是否有网。不同品牌的手机检查网络方式略有不同,但基本上大同小异。一般是向某个固定的地址发起一个HTTP请求,如果请求到正确的数据,则证明这个wifi可以正常连接互联网,如果请求不到数据,或者请求到了错误的数据,则说明这个wifi不能正常连接互联网。

 

上述检查网络的过程可分为三种情况:

 

  • 请求到正常的数据(该wifi可直接上网)

  • 没有请求到数据(该wifi无法连接网络)

  • 请求到错误(非手机期待)的数据(可能需要认证才能连接网络)

 

如果遇到上述第三种情况,那么手机就会打开相应的页面,就是所谓的自动弹出页面。

 

要想实现自动弹出页面,就要制造上述的第三种情况。第一步要捕获手机发起的HTTP请求,第二步向手机返回非手机期待的数据。

 

HTTP请求的过程如上图所示,首先通过DNS获取服务器的IP地址,然后再向服务器发起HTTP请求。

 

以苹果手机为例,连上某个wifi后会访问如下页面

http://captive.apple.com/hotspot-detect.html

 

访问过程如下,先向DNS服务器发起DNS请求,获得captive.apple.com对应的IP地址,然后再向刚刚获取到的地址发送HTTP请求,如果DNS请求失败,则不会(也不能)发起Http请求。

 

手机连上ESP8266模块后,默认的DNS地址就是模块的地址(一般是192.168.4.1),所以我们要在模块上建立一个DNS服务器,响应手机发来的DNS请求,将所有的域名都解析到模块本身的IP地址上,那么手机访问http://captive.apple.com/hotspot-detect.html就相当于访问http://192.168.4.1/hotspot-detect.html,我们的模组就能成功的捕获到手机发来的HTTP请求了。(这种做法通常叫做DNS拦截)。

 

捕获到HTTP请求后,还要做相应的HTTP响应,才能让手机弹出页面。所以模组还需要建立一个HTTP服务器,相应手机发来的HTTP请求。HTTP底层协议是TCP,默认使用的是80端口,我们只需要建立一个TCP服务器,监听80端口,收到数据后做相应的回复即可,关于更具体的HTTP协议的可参考另一篇文章,HTTP协议详解及实践。

 

代码实现


有了上面的理论指导,代码实现起来并不复杂。具体代码我已上传到GitHub(https://github.com/ospanic/Captive_Portal_ESP),可同时适用于ESP8266和ESP32。项目结构如下图所示:

由上图可以看出,程序中主要实现了一个DNS服务器和HTTP服务器,主函数核心逻辑代码代码如下:

 

逻辑很简单,初始化wifi为AP功能后,开启DNS服务及HTTP服务。

 

实际效果


编译后烧录到ESP8266模组,手机搜索名称为“自动弹出页面”的wifi:

连接wifi成功后将自动弹出下图所示页面:

模块运行日志如下图所示:

 

上图中的白色文字,是手机发起的DNS请求,绿色字体中包含HTTP请求的信息。通过日志可以看到不同品牌的手机在自动弹出页面的过程中都发起了那些请求。

如果您觉得有用,点击上图的二维码关注一下呗!

  • 19
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
A:实现这个功能的一种方式是,通过ESP32的Web服务器功能,设置一个指定的网页作为连接成功后自动弹页面。具体步骤如下: 1. 通过Micropython代码,进行WiFi联网; 2. 启动Web服务器,开启监听指定端口; 3. 在Web服务器中设置指定URL对应的处理函数; 4. 在处理函数中,设置自动弹网页(如JavaScript语句:window.open(url)); 5. 将需要弹的网页文件放置到ESP32开发板存储器中。 以下是示例代码: ``` import network import usocket as socket import gc import esp import ure as re esp.osdebug(None) gc.collect() ssid = 'your_wifi_ssid' password = 'your_wifi_password' station = network.WLAN(network.STA_IF) station.active(True) station.connect(ssid, password) while station.isconnected() == False: pass print('Connection successful') print(station.ifconfig()) # Web server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', 80)) s.listen(5) def handler(conn): request = conn.recv(1024) request = str(request) url = re.search('GET /(.+?) HTTP', request) url = url.group(1) print('Content = %s' % url) response = 'HTTP/1.1 200 OK\r\n' response += 'Content-Type: text/html\r\n' response += '\r\n' response += '<html><body><h1>Hello, world %s</h1></body></html>' % url conn.send(response) conn.close() while True: conn, addr = s.accept() print('Got a connection from %s' % str(addr)) handler(conn) ``` 在上面的代码中,Web服务器处理函数handler()会在ESP32接收到HTTP GET请求时被调用。函数会通过正则表达式解析请求的URL,并进行处理。为了让服务器响应弹网页,可以将url作为参数传递给JavaScript的window.open()函数: ``` response = 'HTTP/1.1 200 OK\r\n' response += 'Content-Type: text/html\r\n' response += '\r\n' response += '<html><body><h1>Welcome to my website!</h1></body></html>' response += "<script>window.open('http://%s')</script>" % url ``` 其中,%s会被替换为实际url的值。这样,ESP32连接WiFi成功后,使用浏览器访问ESP32的IP地址时,服务器会响应弹指定的网页,从而实现手机动弹页面的功能。需要注意的是,在ESP32的存储器中存储网页文件时,URL对应的路径需要与正则表达式中提取的URL部分一致。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值