美文网首页NODEMCU物联网nodemcu
9、nodeMCU学习笔记--net模块

9、nodeMCU学习笔记--net模块

作者: 谢mingmin | 来源:发表于2016-11-26 17:24 被阅读5034次

    esp8266  nodemcu  lua  wifi  net  web

    闲言碎语

    nodemcu的wifi模块,花了三篇文章被我水完了。内容还是比较浅显的。不过,nodemcu的开发者确实把wifi模块设计的很简单,也和容易使用。配置函数、station类函数、ap类函数、监听注册函数,总体来讲还是很清晰明了。要用熟这些wifi功能,其实还要配合其他模块一起来(比如这篇文章要说的net),循序渐进。

    模块函数

    net模块的函数也是比较多的。不过,整体结构也是很清晰的。赶紧来看看这些函数

    序号 函数名 参数 返回值
    1 net.createConnection() type, secure net.socket 子模块
    2 net.createServer() type, timeout net.server 子模块
    3 net.multicastJoin() if_ip, multicast_ip
    4 net.multicastLeave() if_ip, multicast_ip
    5 net.server:close()
    6 net.server:listen() port,[ip],function(net.socket)
    7 net.server:on()
    8 net.server:send()
    9 net.socket:close() nil
    10 net.socket:connect() port, ip / domain nil
    11 net.socket:dns() domain, function(net.socket, ip)
    12 net.socket:getpeer() ip, port
    13 net.socket:hold()
    14 net.socket:on() event, function() nil
    15 net.socket:send() string[, function(sent)]
    16 net.socket:unhold()
    17 net.dns.getdnsserver() dns_index(0 / 1) ip
    18 net.dns.resolve() host, function(ip) nil
    19 net.dns.setdnsserver() dns_ip_addr, dns_index nil
    20 net.cert.verify() enable / pemdata true

    参数里面有个type的,只有两种选择,要么net.TCP,要么net.UDP。有几个server相关的API,几个socket相关的API。这里不打算一个一个函数的讲了,直接来几个例子反而更容易理解API的含义。

    实践一下

    光说不练假把式,直接来实践一下。从API中可以知道,net模块可以创建server和client。实践前,确保nodemcu已经连入网络。

    wifi的配置后会一直生效。如果你先前配置过,可以不用配置。当然,重新配置一下也可以。

    client

    这里先来看看如何创建一个client,以及如何进行通信。

    cl = net.createConnection(net.TCP, 0)
    cl:connect(9999, "192.168.199.101")
    cl:on("receive", function(sck, c) print(c) end)
    cl:on("disconnection", function(sck, c) print("disconnection!") end)
    

    使用.createConnection创建一个net.TCP客户端,函数会返回一个socket子模块,后面要用的都是socket相关的函数,第二个参数,1表示加密,0表示不加密。net.socket:connect用来连接到服务端。参数2既可以是ip地址,也可以是域名。注意connect前面用的是冒号:,不是点。接着,找一个网络调试工具来创建一个server。这里我找了个名字叫网络调试的手机APP。net.socket:on函数用来绑定几个事件回调,函数原型是这样的 function(net.socket[, string]):

    • "connection" : 连接;
    • "reconnection" : 重连接;
    • "disconnection" : 断开连接;
    • "receive" : 接收回调,string表示接收到的字符串数据;
    • "sent" : 发送;

    这个例子里面,nodemcu连接到app创建的server后,并没有产生回调事件,具体是什么原因,不清楚。不过,尝试连接到域名却可以产生回调事件。比如下面这个域名

    cl:connect(80, "www.nodemcu.com")
    
    客户端 服务端
    点击APP左边的客户端列表,断开nodemcu,得到一个预期的断开回调。使用.createConnection创建多个客户端。比如,这样子:
    cl = net.createConnection(net.TCP, 0)
    cl2 = net.createConnection(net.TCP, 0)
    

    使用net.socket:send可以向服务端发送数据。比方说在ESPlorer右边的输入框里面输入下面这句语句:

    =cl:send("Hello NodeMCU")
    

    这里需要说明的是,send函数发送的数据长度是有限度的,大概是1400多个字节。当要发送大于1400字节的内容的时候,比如说发送一个带css、js的网页,就需要分成多次发送。多次发送也不是简单的把上面的代码复制几遍就能解决的。而是要用到"sent"事件来回调。

    cnt = 0
    cl = net.createConnection(net.TCP, 0)
    cl:connect(9999, "192.168.199.101")
    cl:on("receive", function(sck, c) print(c) end)
    cl:on("disconnection", function(sck, c) print("disconnection!") end)
    cl:on("sent", function(c) 
        if cnt ~= 10 then
            cl:send(cnt)
            cnt = cnt + 1
        end 
    end)
    

    这个例子可以让客户端在发送完第一条消息后,再发10条消息给服务端。激活的方法还是在ESPlorer中输入一条send语句。

    =cl:send("Hello NodeMCU")
    

    当nodemcu发送完第一条语句后,会触发"sent"事件,进而发送10条消息给服务端。

    收到消息了!

    server

    知道了如何创建并使用一个client后,我们来继续看如何创建一个server。先上个开胃菜。

    ns = net.createServer(net.TCP, 15)
    ns:listen(80, function(c)
        c:on("receive", function(c, d)
            print(d)
            c:send(d)
        end)
        c:on("connection", function(c, d) print(d) end)
        c:on("disconnection", function(c, d) print("disconnection") end)
    end)
    

    使用.createServer创建一个net.TCP的服务端,第二次参数用于设置不活动连接的超时时间,返回一个net.server模块。nodemcu只能创建一个server,不像client可以创建多个。需要注意一下。net.server只有4个函数,其中的send和on仅对udp有用。tcp要使用socket的send和on函数。
      接着用net.server:listen创建一个监听。回调传入的是一个socket。可以尽情的使用socket的函数了,比如用net.socket:on设置各种事件回调。这个例子里面的"connection"依然没效果╮(╯_╰)╭。使用APP连接到创建好的server,试着发送信息。

    这图有点大啊 ╮(╯_╰)╭
      接着到主菜上场了。内容有点长。主要是实现上篇文章说的enduser setup。动筷子前记得把wifi模式设置成AP模式或者混合模式。
    web = '<!doctype html><html><head><meta charset=\'utf-8\'><meta name=\'viewport\'content=\'width=380\'><title>Connect gadget to you WiFi</title><style media=\'screen\'type=\'text/css\'>*{margin:0;padding:0}html{height:100%;background:linear-gradient(rgba(196,102,0,0.2),rgba(155,89,182,0.2)),url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAA8AgMAAACm+SSwAAAADFBMVEVBR1FFS1VHTlg8Q0zU/YXIAAADVElEQVQ4yy1TTYvTUBQ9GTKiYNoodsCF4MK6U4TZChOhiguFWHyBFzqlLl4hoeNvEBeCrlrhBVKq1EUKLTP+hvi1GyguXqBdiZCBzGqg20K8L3hDQnK55+OeJNguHx6UujYl3dL5ALn4JOIUluAqeAWciyGaSdvngOWzNT+G0UyGUOxVOAdqkjXDCbBiUyjZ5QzYEbGadYAi6kHxth+kthXNVNCDofwhGv1D4QGGiM9iAjbCHgr2iUUpDJbs+VPQ4xAr2fX7KXbkOJMdok965Ksb+6lrjdkem8AshIuHm9Nyu19uTunYlOXDTQqi8VgeH0kBXH2xq/ouiMZPzuMukymutrBmulUTovC6HqNFW2ZOiqlpSXZOTvSUeUPxChjxol8BLbRy4gJuhV7OR4LRVBs3WQ9VVAU7SXgK2HeUrOj7bC8YsUgr3lEV/TXB7hK90EBnxaeg1Ov15bY80M736ekCGesGAaGvG0Ct4WRkVQVHIgIM9xJgvSFfPay8Q6GNv7VpR7xUnkvhnMQCJDYkYOtNLihV70tCU1Sk+BQrpoP+HLHUrJkuta40C6LP5GvBv+Hqo10ATxxFrTPvNdPr7XwgQud6RvQN/sXjBGzqbU27wcj9cgsyvSTrpyXV8gKpXeNJU3aFl7MOdldzV4+HfO19jBa5f2IjWwx1OLHIvFHkqbBj20ro1g7nDfY1DpScvDRUNARgjMMVO0zoMjKxJ6uWCPP+YRAWbGoaN8kXYHmLjB9FXLGOazfFVCvOgqzfnicNPrHtPKlex2ye824gMza0cTZ2sS2Xm7Qst/UfFw8O6vVtmUKxZy9xFgzMys5cJ5fxZw4y37Ufk1Dsfb8MqOjYxE3ZMWxiDcO0PYUaD2ys+8OW1pbB7/e3sfZeGVCL0Q2aMjjPdm2sxADuejZxHJAd8dO9DSUdA0V8/NggRRanDkBrANn8yHlEQOn/MmwoQfQF7xgmKDnv520bS/pgylP67vf3y2V5sCwfoCEMkZClgOfJAFX9eXefR2RpnmRs4CDVPceaRfoFzCkJVJX27vWZnoqyvmtXU3+dW1EIXIu8Qg5Qta4Zlv7drUCoWe8/8MXzaEwux7ESE9h6qnHj3mIO0/D9RvzfxPmjWiQ1vbeSk4rrHwhAre35EEVaAAAAAElFTkSuQmCC)}body{font-family:arial,verdana}div{position:absolute;margin:auto;top:0;right:0;bottom:0;left:0;width:320px;height:274px}form{width:320px;text-align:center;position:relative}form fieldset{background:white;border:0 none;border-radius:5px;box-shadow:0 0 15px 1px rgba(0,0,0,0.4);padding:20px 30px;box-sizing:border-box}form input{padding:15px;border:1px solid#ccc;border-radius:3px;margin-bottom:10px;width:100%;box-sizing:border-box;font-family:montserrat;color:#2C3E50;font-size:13px}form.action-button{width:100px;background:#27AE60;font-weight:bold;color:white;border:0 none;border-radius:3px;cursor:pointer;padding:10px 5px;margin:10px 5px}form.action-button:hover,#msform.action-button:focus{box-shadow:0 0 0 2px white,0 0 0 3px#27AE60}.fs-title{font-size:15px;text-transform:uppercase;color:#2C3E50;margin-bottom:10px}.fs-subtitle{font-weight:normal;font-size:13px;color:#666;margin-bottom:20px}</style></head><body><div><form><fieldset><h2 class=\'fs-title\'>WiFi Login</h2><h3 class=\'fs-subtitle\'>Connect gadget to your WiFi</h3><input type=\'text\'autocorrect=\'off\'autocapitalize=\'none\'name=\'wifi_ssid\'placeholder=\'WiFi Name\'/><input type=\'password\'name=\'wifi_password\'placeholder=\'Password\'/><input type=\'submit\'name=\'save\'class=\'submit action-button\'value=\'Save\'/></fieldset></form></div></body></html>'    
    
    sendBuf = {}
    
    for i = 1, #web, 1400 do
        local len = #web - i 
        if len > 1400 then
            sendBuf[#sendBuf + 1] = string.sub(web, i, i+1400-1)
        else
            sendBuf[#sendBuf + 1] = string.sub(web, i, i+len)
        end
    end
    

    web数组存储了一个web页面。当然了,这个web页面比较大,远远超过了1400字节。需要将它分成几块,以便后面分批发送。所以,把这个web页面分块存储到一个table中。

    function sendWeb(c)
        if #sendBuf > 0 then
            s = table.remove(sendBuf, 1)
            c:send(s)
        else
            c:close()
        end
    end
    

    函数sendWeb用来把table里面的内容发送出去,一边发送,一边remove表里面的内容,所以用浏览器浏览只能打开页面一次 o(╯□╰)o。或许这个地方可以优化一下。

    sv = net.createServer(net.TCP, 60)
    
    sv:listen(80, function(c)
        c:on("receive", function(cn, req)
            local _, _, method, path, vars = string.find(req, "([A-Z]+) (.+)?(.+) HTTP")
            if method == nil then
                _, _, method, path = string.find(req, "([A-Z]+) (.+) HTTP")
            end
    
            local _GET = {}
            if vars ~= nil then
                for k, v in string.gmatch(vars, "(%w+_%w+)=(%w+)&*") do
                    _GET[k] = v
                    print(k .. ":" .. v)
                end        
                local sendbuf = "<h1>Config Succeed!</h1>"
                sendbuf = sendbuf.."<p>wifi_ssid: ".._GET["wifi_ssid"].."</P>"
                sendbuf = sendbuf.."<p>wifi_password  :".._GET["wifi_password"].."</P>"
                cn:send(sendbuf)
                cn:close()
            else
                cn:on("sent", sendWeb)
                sendWeb(cn)
            end
        end)
    end)
    

    最后这一部分,和开胃菜那个例子的效果差不多,只是这回发送的是一个页面。回调函数中,先解析浏览器get过来的内容,之后把类似于这种格式的字符串("wifi_ssid=hello&wifi_password=12345678")存储到一个table中。最后又把提取到的内容send出来,赶紧用浏览器访问nodemcu看看效果吧。只需要在浏览器的地址栏输入ip地址即可。
      利用net的server,还可以显示web控制led之类的效果,网上有相关的例子。或者可以配合nodemcu上面的AD完成更多东西来。不过前提是,要能写出漂亮的web页面o(╯□╰)o。

    一点lua语法

    local _, _, method, path, vars = string.find(req, "([A-Z]+) (.+)?(.+) HTTP")
    

    这个地方的 _ 实际上是一个变量,叫虚变量,因为string的find方法会返回子串的起始和结束地址。不需要的话,可以用虚变量来存储。

    相关文章

      网友评论

      • aed8c50ca94c:请问楼主吗,如果是两个nodeMCU通信,采用的是tcp server和client这种,那么client如何获得server的ip呢?
        谢mingmin:@hemycoo 那我就不懂了。串口的话就广播咯。就算是电脑,你也要知道网址才能访问网站吧
        aed8c50ca94c:@谢mingmin 不行啊,要让它们自动化,人为的话,那用noedemcu这种破玩意儿,干啥
        谢mingmin:@hemycoo 人为找出server的IP😅
      • ReCclay:为什么前提一定要先连入网络?
        谢mingmin: @ReCclay WiFi
        ReCclay:@谢mingmin 是先连电脑上的wifi再连手机上的server?
        谢mingmin: @ReCclay 没连入网络怎么有IP啊
      • 黑小马_:楼主,我想问问,如何处理多个客服端连接,他不像java一样把每个连接对象存于容器里进行管理!
        谢mingmin: @黑小马_AIDE 这个不懂😂
        黑小马_:还有一个问题就是,我close后程序直接死了,重启!
        谢mingmin: @黑小马_AIDE 额,没试过,这种东西没法和PC比的
      • 6b04d255d5c7:不知为何,在写入web字符串的时候总是报错,但是在lua环境下就没有问题

        谢mingmin: @klzw43 是不是编码问题
      • Zszen:你好请问nodemcu不通过服务器, 能否直接给苹果发送推送通知, 这样能省一个服务器
        谢mingmin: @Zszen 这些我就不懂了😅在iOS上跑个服务?
      • a8d1c98e91c9:你好,我用NodMCU做服务器可以与调试助手连接了,但做客户端,调试助手就找不到啊,请问怎么解决的。
        谢mingmin: @wlb_eb00 那在换一个试试看
        a8d1c98e91c9:@谢mingmin 设置了,好像说调试助手有BUG,做服务器就连接不上
        谢mingmin: @wlb_eb00 调试助手有设置成服务端没?
      • 清晨_e16d:把nodemcu做服务器,树莓派做客户端,两者之间怎么传输数据呢?
        谢mingmin: @清晨_e16d 树莓派上用node.js什么的写个客户端连nodemcu上的服务端就可以了吧
      • 问号_ac26:你好,我将esp8266设置为TCP服务器时,能否对客户端发送信息,关于 net.socket:on(sent )这部分内容好少,不太会用
        谢mingmin: @问号_ac26 你删了,客户端发送消息后会接受到服务端返回的消息吗?
        问号_ac26:@谢mingmin ns:listen(80, function(c)
        c:on("receive", function(c, d)
        print(d)
        c:send(d)
        end)
        c:send(d)是为了什么,我删除貌似没什么变换
        谢mingmin: @问号_ac26 on函数是事件绑定用,发送用c:send(d)。花点时间看看API,可以帮你省不少时间的。
      • 迗賜壹嚉c:楼主我这怎么nodemcu下问程序后会直接显示disconnection,网络助手就没连上过
        迗賜壹嚉c:@谢mingmin 还有什么要注意的吗?弄了半天了
        迗賜壹嚉c:@谢mingmin 设置了
        谢mingmin: @迗賜壹嚉c 那个,WiFi配置了么?
      • e0d55f752d1e:如何检测Nodemcu的TCP客户端套接字成功连接服务端?
        谢mingmin: @ma6254 咋解决的,能分享一下吗
        e0d55f752d1e:@谢mingmin 好吧😓 我已经找到了解决方案
        谢mingmin: @ma6254 连IP的方式没有回调,如果是域名的话,连接成功会有回调。可能是bug,不知道新版本有木有解决😅

      本文标题:9、nodeMCU学习笔记--net模块

      本文链接:https://www.haomeiwen.com/subject/wchkpttx.html