Luat例項教程:tcp短連線

weixin_34054866發表於2018-03-23

本示例實現的功能是:基於TCP的socket短連線通訊demo專案(UDP使用方式和TCP完全相同)。

功能描述:

1、每隔10秒鐘傳送一次位置包"loc data\r\n"到後臺,無論傳送成功或者失敗,5秒後都斷開連線;

2、收到後臺的資料時,在rcv函式中列印出來

測試時請搭建自己的伺服器,並且修改下面的PROT,ADDR,PORT,支援域名和IP地址

!!!attention

長連線和短連線的差別:長連線不會自動斷開,短連線傳送訊息之後會自動斷開連線

1.在編輯工具建立一個test.lua的檔案(不一定叫這個名字,使用者可以自己隨便取名)

2.設定本檔案被全體可見。也就意味著,一旦test被某一檔案載入,則test在任何檔案中均可被看見,即test中全域性變數和函式均可被任何檔案呼叫。

module(...,package.seeall)

3.test檔案頭需要 require "xxx" 模組。載入後,就可以呼叫xxx.lua庫檔案中的全域性變數和函式了

require"socket"

4.定義print函式,除錯用

local function print(...)
    _G.print("test",...)
end

5.定義相關引數

local ssub,schar,smatch,sbyte,slen = string.sub,string.char,string.match,string.byte,string.len
--測試時請搭建自己的伺服器
local SCK_IDX,PROT,ADDR,PORT = 1,"TCP","120.26.196.195",9999
--linksta:與後臺的socket連線狀態
local linksta
--是否成功連線過伺服器
local hasconnected
--開機後如果一次也沒有連線上後臺,會有如下異常處理
--一個連線週期內的動作:如果連線後臺失敗,會嘗試重連,重連間隔為RECONN_PERIOD秒,最多重連RECONN_MAX_CNT次
--如果一個連線週期內都沒有連線成功,則等待RECONN_CYCLE_PERIOD秒後,重新發起一個連線週期
--如果連續RECONN_CYCLE_MAX_CNT次的連線週期都沒有連線成功,則重啟軟體
local RECONN_MAX_CNT,RECONN_PERIOD,RECONN_CYCLE_MAX_CNT,RECONN_CYCLE_PERIOD = 3,5,3,20
--reconncnt:當前連線週期內,已經重連的次數
--reconncyclecnt:連續多少個連線週期,都沒有連線成功
--一旦連線成功,都會復位這兩個標記
--conning:是否在嘗試連線
local reconncnt,reconncyclecnt,conning = 0,0

6.建立到後臺伺服器的連線

--[[
函式名:connect
功能  :建立到後臺伺服器的連線;
        如果資料網路已經準備好,會理解連線後臺;否則,連線請求會被掛起,等資料網路準備就緒後,自動去連線後臺
        ntfy:socket狀態的處理函式
        rcv:socket接收資料的處理函式
引數  :無
返回值:無
]]
function connect()
    socket.connect(SCK_IDX,PROT,ADDR,PORT,ntfy,rcv)
    conning = true
end

7.連線成功,編寫socket處理函式。

--[[
函式名:ntfy
功能  :socket狀態的處理函式
引數  :
        idx:number型別,socket.lua中維護的socket idx,跟呼叫socket.connect時傳入的第一個引數相同,程式可以忽略不處理
        evt:string型別,訊息事件型別
        result: bool型別,訊息事件結果,true為成功,其他為失敗
        item:table型別,{data=,para=},訊息回傳的引數和資料,目前只是在SEND型別的事件中用到了此引數,例如呼叫socket.send時傳入的第2個和第3個引數分別為dat和par,則item={data=dat,para=par}
返回值:無
]]
function ntfy(idx,evt,result,item)
    print("ntfy",evt,result,item,hasconnected)
    --連線結果(呼叫socket.connect後的非同步事件)
    if evt == "CONNECT" then
        conning = false
        --連線成功
        if result then
            reconncnt,reconncyclecnt,linksta = 0,0,true
            --停止重連定時器
            sys.timer_stop(reconn)
            --開機後第一次連線成功
            if not hasconnected then
                hasconnected = true
                --傳送位置包到後臺
                locrpt()
            end
        --連線失敗
        else
            if not hasconnected then
                --5秒後重連
                sys.timer_start(reconn,RECONN_PERIOD*1000)
            else                
                link.shut()
            end         
        end 
    --資料傳送結果(呼叫socket.send後的非同步事件)
    elseif evt == "SEND" then
        if item then
            sndcb(item,result)
        end
    --連線被動斷開
    elseif evt == "STATE" and result == "CLOSED" then
        linksta = false
        --補充自定義功能程式碼
    --連線主動斷開(呼叫link.shut後的非同步事件)
    elseif evt == "STATE" and result == "SHUTED" then
        linksta = false
        --補充自定義功能程式碼
    --連線主動斷開(呼叫socket.disconnect後的非同步事件)
    elseif evt == "DISCONNECT" then
        linksta = false
        --補充自定義功能程式碼         
    end
    --其他錯誤處理
    if smatch((type(result)=="string") and result or "","ERROR") then
        --斷開資料鏈路,重新啟用
        link.shut()
    end
end

--[[
函式名:rcv
功能  :socket接收資料的處理函式
引數  :
        idx :socket.lua中維護的socket idx,跟呼叫socket.connect時傳入的第一個引數相同,程式可以忽略不處理
        data:接收到的資料
返回值:無
]]
function rcv(idx,data)
    print("rcv",data)
end

8.傳送函式,heartrpt()以及對應的回撥函式,在回撥函式中關閉連線。

--[[
函式名:snd
功能  :呼叫傳送介面傳送資料
引數  :
        data:傳送的資料,在傳送結果事件處理函式ntfy中,會賦值到item.data中
        para:傳送的引數,在傳送結果事件處理函式ntfy中,會賦值到item.para中 
返回值:呼叫傳送介面的結果(並不是資料傳送是否成功的結果,資料傳送是否成功的結果在ntfy中的SEND事件中通知),true為成功,其他為失敗
]]
function snd(data,para)
    return socket.send(SCK_IDX,data,para)
end

--[[
函式名:locrpt
功能  :傳送位置包資料到後臺
引數  :無 
返回值:無
]]
function locrpt()
    print("locrpt",linksta)
    --if linksta then
        if not snd("loc data\r\n","LOCRPT") then locrptcb({data="loc data\r\n",para="LOCRPT"},false) end    
    --end
end

--[[
函式名:locrptcb
功能  :位置包傳送結果處理,啟動定時器,10秒鐘後再次傳送位置包2
引數  :  
        result: bool型別,傳送結果或者是否超時,true為成功或者超時,其他為失敗
        item:table型別,{data=,para=},訊息回傳的引數和資料,例如呼叫socket.send時傳入的第2個和第3個引數分別為dat和par,則item={data=dat,para=par}
返回值:無
]]
function locrptcb(item,result)
    print("locrptcb",linksta)
    --if linksta then
        --5秒後再去斷開socket連線,這5秒內用來接收伺服器下發的資料
        sys.timer_start(socket.disconnect,5000,SCK_IDX)
        sys.timer_start(locrpt,10000)
    --end
end

--[[
函式名:sndcb
功能  :傳送資料結果事件的處理
引數  :  
        result: bool型別,訊息事件結果,true為成功,其他為失敗
        item:table型別,{data=,para=},訊息回傳的引數和資料,例如呼叫socket.send時傳入的第2個和第3個引數分別為dat和par,則item={data=dat,para=par}
返回值:無
]]
local function sndcb(item,result)
    print("sndcb",item.para,result)
    if not item.para then return end
    if item.para=="LOCRPT" then
        locrptcb(item,result)
    end
    if not result then link.shut() end
end

9.斷線自動重連

--[[
函式名:reconn
功能  :重連後臺處理
        一個連線週期內的動作:如果連線後臺失敗,會嘗試重連,重連間隔為RECONN_PERIOD秒,最多重連RECONN_MAX_CNT次
        如果一個連線週期內都沒有連線成功,則等待RECONN_CYCLE_PERIOD秒後,重新發起一個連線週期
        如果連續RECONN_CYCLE_MAX_CNT次的連線週期都沒有連線成功,則重啟軟體
引數  :無
返回值:無
]]
local function reconn()
    print("reconn",reconncnt,conning,reconncyclecnt)
    --conning表示正在嘗試連線後臺,一定要判斷此變數,否則有可能發起不必要的重連,導致reconncnt增加,實際的重連次數減少
    if conning then return end
    --一個連線週期內的重連
    if reconncnt < RECONN_MAX_CNT then
        reconncnt = reconncnt+1
        link.shut()
        connect()
    --一個連線週期的重連都失敗
    else
        reconncnt,reconncyclecnt = 0,reconncyclecnt+1
        if reconncyclecnt >= RECONN_CYCLE_MAX_CNT then
            sys.restart("connect fail")
        end
        sys.timer_start(reconn,RECONN_CYCLE_PERIOD*1000)
    end
end

10.定時器啟動connect()函式

sys.timer_start(connect,2000)

11.在編輯工具中建立一個名為main.lua的檔案。lua指令碼的執行從main.lua開始,main.lua是入口檔案(注意:main.lua只能有一個)。在main.lua中把test載入進去就好了。sys.init()是對系統初始化,sys.run()是系統主程式。這兩句必須有。

--重要提醒:必須在這個位置定義MODULE_TYPE、PROJECT和VERSION變數
--MODULE_TYPE:模組型號,目前僅支援Air201、Air202、Air800
--PROJECT:ascii string型別,可以隨便定義,只要不使用,就行
--VERSION:ascii string型別,如果使用Luat物聯雲平臺韌體升級的功能,必須按照"X.X.X"定義,X表示1位數字;否則可隨便定義
MODULE_TYPE = "Air202"
PROJECT = "SOCKET_SHORT_CONNECTION"
VERSION = "1.0.0"
require"sys"
require"test"
if MODULE_TYPE=="Air201" then
require"wdt"
end
sys.init(0,0)
sys.run()

!!!attention

一個工程只有一個main.lua

相關文章