NODEMCU调试心得7 - 关于网络协议 HTTP 2

时间:2021-04-20 13:42:28

关于网络协议 HTTP 2

上一节,我们用nodemcu服务器向客户端发送nodemcu的内存信息。这一节反过来,我们介绍如何用客户端控制nodemcu。

先介绍一个简单的例子,用客户端控制nodemcu的GPIO4,实现nodemcu的蓝色LED远程开关。


Step 3

这里仍然参考了 ckuehnel代码gpio.lua

  • 下面是我的代码,取名叫My_gpio.lua

-- SSID = " "
-- password = " "
pin = 4

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

srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
conn:on("receive", function(conn,payload)
print(payload)

local _, _, method, path, vars = string.find(payload, "([A-Z]+) (.+)?(.+) HTTP");
if(method == nil)then
_, _, method, path = string.find(payload, "([A-Z]+) (.+) HTTP");
end
local _GET = {}
if (vars ~= nil)then
for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
_GET[k] = v
end
end

-- HTML
local buf = "";
buf = buf.."<h1> Web Server</h1>";
buf = buf.."<p>LIGHT 1 "
buf = buf.."<a href=\"?pin=ON4\"><button>LED OFF</button></a>&nbsp;"
buf = buf.."<a href=\"?pin=OFF4\"><button>LED ON</button></a></p>";

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

conn:send(buf);
conn:close();
collectgarbage();
end)
end)

逐段来看代码

  • 前两行,定义路由器的账号与密码,因为我的nodemcu已经默认连上路由,所有这里就屏蔽了。
    • 注意,当你连上路由,获得IP地址后,这个信息就保存在flash里了。所以就算掉电重启,还会自动连接
  • 第三行,pin = 4定义GPIO,4号对应的是nodemcu上控制LED的管脚,注意高电平时LED关,低电平开。
  • 第四行和第五行,设置esp8266为station模式,连接路由器。同样之前连接过,信息就保存了,所以就屏蔽了。
  • print()打印IP地址信息,这个是客户端浏览器要访问nodemcu,就要输它的IP地址。
    • 每次上电时的IP有可能不一样
  • gpio.mode()设置gpio为输出模式,也就是控制模式

  • srv=..建立TCP服务器

  • srv.listen()监听80端口的信息,80也就是http协议对应的端口
  • conn.on("recieve"..)这个与上一节的例程一样,也是接收客户端发出的信息payload。不同之处在于:
    • 上节的例程只接收客户端连接到服务器的信息。
    • 本节的例程除了接收连接信息之外,还接收客户端发送的gpio控制指令
  • print()打印客户端发来的payload信息,会有四五行,具体内容可以见上一节关于网络协议 HTTP,最重要的是第一行,里面包含的信息需要提取出来,一般是这样

      GET / HTTP/1.1

    或者这样

      GET /?pin=ON4 HTTP/1.1
  • string.find()函数用来提取控制指令,它会返回符合规则的字符串,這里的目标信息是客户端发送的控制指令,一般为如下形式:

    GET /?pin=ON4 HTTP/1.1

    这里定义了3个字符串method,path,vars.

    • method代表客户端的方法,有GETPOST等等。GET用来向服务器发送请求的信息,POST用来向服务器提交网页上输入的信息。
    • path 代表路径,这里只得到一个/
    • vars 代表控制按钮button返回的控制信息

    • 前面的两个_,_是模糊变量,这两个变量是返回find函数找到字符串的第一个和最后一个的index(没什么用)

    find()的第一个变量是要检索的字符串,第二个变量是检索的规则

    • method,path,vars对应的检索规则分别是([A-Z]+),(.+), (.+)
    • ([A-Z]+),[A-Z]代表检索的是从A到Z的大写字母,+代表不是单个字母,而是多个字符构成的字符串
    • (.+).代表检索所有字符,+同样代表是字符串
    • HTTP是格式,代表pathvars之间有?隔开,vars后以HTTP结束,相当于pathvar的读取终止符

    假如是客户端刚连接时发送的信息,paylaod第一行是这样

    GET / HTTP/1.1

    不符合刚才带?的格式,methodpath,vars 都得到空值nil

  • if..用来判断method是否为nil,如果是,说明是客户端的连接信息,

  • find()的检索模式改为([A-Z]+) (.+) HTTP,只接收两个字符串methodpath

  • 定义局域变量_GET,接收vars中的gpio控制信息

  • 如果vars不为空,其代表了控制信息
  • 用for循环提取pin=ON4里的信息
  • string.gmatch()是专用在迭代forx循环中的字符串函数,
    • (%w+)=(%w+)&*,%w代表字母和数字,+代表是多个字符。
    • &应该是和=一样,是格式,
    • *的作用和+类似,区别是* 代表该字符可以不出现。&*的意思是vars字符串的提取格式最后可以没有&,也可以有一个或者多个&
更多字符串函数和模式的语法,请点击这里这里

接下来是HTML代码

  • 首先定义一个空字符串
  • 接下来直接写HTML的body部分,前面没有head,也没有<html>,<head>,<body>关键字,直接写标题1:<h1>..</h1>
  • 然后写第一端的文字部分,<p1>..
  • 接下来的代码定义button和它的返回值。关键字<a>通常是链接的关键字,href=后是链接的地址。但是这里,用来建立一个button对象,

    • href = \"?pin=ON4\"\是转义符,当按钮被按下,客户端就返回?pin=ON4这一信息,
    • <button>LED OFF</button>LED OFF是button对象上的文字标签
    • </a>:结束<a>开头的代码,&nbsp代表一个空格
    • 这行代码的含义是点击LED ON开启LED,gpio4处于低电位
  • 下一段代码的含义是:点击LED OFF关闭LED。点击后,返回?pin=OFF4
    • 文字,按钮on和按钮off都属於段落p1,处在同一行上

接下来,对vars代表的字符串pin=ON4进行判断,

  • pin作为关键字,ON4或者OFF4是关键字对应的元素。
  • 如果_GET.pin对应的元素是ON4,拉高GPIO电平,反之,则拉低

最后发送buf包,并关闭conn连接,清理内存垃圾

  • 注意,这里并没有像Step 1的例程,没有加入HTTP的头部信息,可能是传送buff量比较小的原因,而传送的HTML文档本身就是一个很简略的版本。

    如果传送buff比较长,或者对传送正确率要求比较高,还是采用标准的HTML格式和HTML协议头部信息。

  • 这里直接用conn:close()关闭连接,没有像上一个例程,采用conn:on("sent"..)事件发生函数。


Step 4

代码解释完后,我们上传到nodemcu。如果你的nodemcu没有连上路由,或者刚刷了固件,就取消My_gpio.lua的注释,输入你的wifi账号和密码
- Save to ESP后,会打印你的IP信息,像这样

  172.21.100.79 255.255.255.0   172.21.100.254

第一个就是nodemcu的IP,后面分别是子网掩码,和路由器的的IP

  • 在浏览器里输入nodemcu的IP,出现界面,可以选择关闭和开启nodemcu的LED,像这样

NODEMCU调试心得7 - 关于网络协议 HTTP 2
NODEMCU调试心得7 - 关于网络协议 HTTP 2

  • 因为HTML没有Head信息,所以标签上显示的和地址栏信息是一样的
  • 点击LED OFF,原先的IP地址就会附加信息:/?pin=ON4,作为客户端的GET信息


本来还打算讲一个提交表单的例程,不过已经写得很长了,就放到下一节好了。顺带会讲一讲CSS,让没有图片的服务器也能exciting。