NODEMCU调试心得8 - 关于网络协议 HTTP 3 之web框架

时间:2022-03-15 13:55:35

关于网络协议 HTTP 3 之web框架

这一节应该算外传的性质。得到qq群里会飞的小猪启发,打算写一个web框架,处理比较复杂的服务器响应。

之前的例程中,Html和图片的代码都是嵌入到程序中。而一般情况中,Html文档和图片都是单独存放的。

所谓web框架

  • ,简单说,就是接收客户端的HTTP请求,返回相应的资源,可以是另一个html文件,或者是图片等等。

下面给的例程是一个非常简单的框架,只处理GET请求,服务器由两个html文档,一个png图片,一个服务器图标favicon.ico组成。

  • 主页Home.html嵌入png图片
  • LED.html 可以控制esp-12的LED
  • 两个html文档都包含彼此的链接
  • favicon.ico出现在标签页上,收藏时作为标识

接上节

Step 5

先分析服务器代码,名称为web_frame.lua

  SSID = " "
password = " "
pin = 4

-- wifi.setmode(wifi.STATION)
wifi.sta.config(SSID,password)
gpio.mode(pin, gpio.OUTPUT)

srv=net.createServer(net.TCP,30)
srv:listen(80,function(conn)
conn:on("receive", function(conn,payload)
print(payload)
local _, _, method, vars = string.find(payload, "([A-Z]+) /(.+) HTTP");
print(method,vars);
local filename = nil

if (vars == nil) then filename = "Home.html"
elseif (vars ~= nil) then
local _, _, key, value = string.find(vars,".*?(%w+)=(%w+).*")

if (value ~= nil) then
if (value =="ON4") then gpio.write(pin, gpio.HIGH);
elseif (value =="OFF4") then gpio.write(pin, gpio.LOW);
end

filename = "LED.html"

elseif (value == nil) then filename = vars
end
end

print(filename)
file.open(filename,"r")
local length = file.seek("end")
file.seek("set")

local function send ()
if (file.seek("cur") == length ) then
conn:close()
file.close()
print(filename.." has been sent.")
else
local buf2 = file.read(1024)
conn:send(buf2)
print(file.seek("cur"))
end
end

send()
conn:on("sent", send)

end)
end)

print(wifi.sta.getip())
  • 见过很多次的代码就不详细解释了,首先设定wifi账号和密码,设定LED的gpio管脚,连接路由,设置gpio为输出模式,创建TCP服务器,延迟30秒。然后监听HTTP端口80,注册recieve事件,接收client的请求
  • 解析client请求的第一行,因为nodemcu的文件系统不支持文件夹,所以路径没有用,只解析method和客户端的返回值vars
  • vars分为几种情况,下面的代码进行解析

    • 如果客户端刚连上服务器(在浏览器地址栏输入nodemcu的IP,回车),payloadGET / HTTP/1.1,vars为空,定义发送的文件名称filename为主页Home.html
    • 如果client发出请求,vars不为空
    • vars可能是按钮发出的请求命令%22?pin=ON4\%22,用string.findpinON4解析出来,赋值给变量keyvalue
    • 根据value的值,调用gpio函数,控制LED
    • 同时,filename =LED.html,发送LED.html文档
    • 如果vars是像Home.html,nodemcu.png这样的文件名,value为空,filename = vars。把vars作为要发送的文件名

    • 上述代码定义了服务器要发送的文件,接下来打开filename文件读取

    • file.seek(“end”)得到文件的长度,此时读取位置移到文件的末尾
    • file.seek(“set”)重新定位到文件开头

    • 接下来定义本地函数send(),分段读取文件并发送,根据词法作用域,本地函数内部可直接调用上层函数的变量

    • file.seek(“cur”)是当前读取的位置,如果等于文件长度,代表已读取完毕,关闭filename文件,同时关闭conn连接
    • 如果没有读取完毕,就读取1024个字符,并用conn:send()发送

    • 调用本地函数send() ,发送第一段文件片段

    • conn:on("sent",send),注册了一个”sent”事件,每次发送一段后,就调用send()发送第二段命令,第二段发送后又会再次调用发第三段,直到全部发送完毕,关闭conn。
    • 上述过程是一个send()函数的递归调用
    • conn:on("sent",send)只是注册事件,只有满足条件才会执行,它和send()代码的顺序是可以颠倒的,参考代码里,send()就放在了后面。

关于这段代码,多说几点

  • 服务器发送的respond信息,也就是conn:send发送的信息,没有包括HTTP的头部信息,可能是因为懒吧
  • 如果请求的文件不存在,程序会报错,比如你g忘了上传favicon.ico,程序会报错退出,因为我没有加判断机制,避免打开一个不存在的文件,也是因为懒吧
  • 这种发送方式算是官方推荐的方式,可以发送很大的文件,这里的png文件有23k,大小远超出了空闲的内存,据qq群大佬们说,发送2M的文件都没有问题。
  • 连续用conn:send发送,官方说是会报错的,我之前的例程就是用连续发送(傲娇脸),我自己的经验是:在传小于3~4k的文件还行,如果是传大图片就传不完了,官方的说法是乐鑫原厂固件的设定。所以还是推荐官方默认的方法
  • 两个Html文档都是用上节的例程改的,这里就不解释了。我把这几个文件都和这篇文档一起上传了

Step 6

  • 分别上传 web_frame.lua, Home.html, LED.html, nodemcu.png, favicon.ico, 记得SSID和password改成自己路由器的
  • dofile(“web_fram.lua”),在浏览器输入nodemcu的IP,如果没有给出,就手动输入print(wifi.sta.getip()),nodemcude的IP是第一个
  • 主页界面是这样,下方有LED.html的链接

NODEMCU调试心得8 - 关于网络协议 HTTP 3 之web框架
NODEMCU调试心得8 - 关于网络协议 HTTP 3 之web框架

在标签上可以看到 favicon.ico图标

  • 点击下方的LED.html,跳转页面,点击按钮可以控制LED

    NODEMCU调试心得8 - 关于网络协议 HTTP 3 之web框架

  • 这个服务器在firfox,IE edge,chrom上测试通过,但是360浏览器没法控制LED,也许任务我这个服务不安全,不返回”?pin=ON4”信息(神秘的微笑)

这节就到这里了,如果你想搞n个html页面外加一堆图片的exciting服务器,可以试试这个框架。