Add telnet example
This commit is contained in:
parent
14c1b8fd54
commit
ba03cb0ef4
|
@ -6,42 +6,19 @@
|
||||||
| 2018-05-24 | [Terry Ellison](https://github.com/TerryE) | [Terry Ellison](https://github.com/TerryE) | [telnet.lua](./telnet.lua) |
|
| 2018-05-24 | [Terry Ellison](https://github.com/TerryE) | [Terry Ellison](https://github.com/TerryE) | [telnet.lua](./telnet.lua) |
|
||||||
|
|
||||||
|
|
||||||
The Lua telnet example previously provided in our distro has been moved to this
|
This README discusses the packet marshalling versions of telnet. The first (fifosock)
|
||||||
file `simple_telnet.lua` in this folder. This README discusses the version complex
|
version was written for SDK 2 implementations, with all of the marshalling imlemented
|
||||||
implementation at the Lua module `telnet.lua`. The main reason for this complex
|
in Lua; the second (pipe) version uses the latest features added to the SDK 3 version
|
||||||
alternative is that a single Lua command can produce a LOT of output, and the
|
that have been added to prepare for the `lua53` implementation. These exploit the
|
||||||
telnet server has to work within four constraints:
|
stdin / stdout pipe functionality and task integration that is now build into the
|
||||||
|
NodeNMCU Lua core.
|
||||||
|
|
||||||
- The SDK rules are that you can only issue one send per task invocation, so any
|
There are two nice advantages of this core implementation:
|
||||||
overflow must be buffered, and the buffer emptied using an on:sent callback (CB).
|
|
||||||
|
|
||||||
- Since the interpeter invokes a node.output CB per field, you have a double whammy
|
- Errors are now written to stdout in a spearate task execution.
|
||||||
that these fields are typically small, so using a simple array FIFO would rapidly
|
- The pipes pretty much eliminate uart and telnet overrun.
|
||||||
exhaust RAM.
|
|
||||||
|
|
||||||
- For network efficiency, the module aggregates any FIFO buffered into sensible
|
Both have the same interface if required into the variable `telnet`
|
||||||
sized packet, say 1024 bytes, but it must also need to handle the case when larger
|
|
||||||
string span multiple packets. However, you must flush the buffer if necessary.
|
|
||||||
|
|
||||||
- The overall buffering strategy needs to be reasonably memory efficient and avoid
|
|
||||||
hitting the GC too hard, so where practical avoid aggregating small strings to more
|
|
||||||
than 256 chars (as NodeMCU handles \<256 using stack buffers), and avoid serial
|
|
||||||
aggregation such as buf = buf .. str as this hammers the GC.
|
|
||||||
|
|
||||||
So this server adopts a simple buffering scheme using a two level FIFO. The
|
|
||||||
`node.output` CB adds records to the 1st level FIFO until the #recs is \> 32 or the
|
|
||||||
total size would exceed 256 bytes. Once over this threashold, the contents of the
|
|
||||||
FIFO are concatenated into a 2nd level FIFO entry of upto 256 bytes, and the 1st
|
|
||||||
level FIFO cleared down to any residue.
|
|
||||||
|
|
||||||
The sender dumps the 2nd level FIFO aggregating records up to 1024 bytes and once this
|
|
||||||
is empty dumps an aggrate of the 1st level.
|
|
||||||
|
|
||||||
Lastly remember that owing to architectural limitations of the firmware, this server
|
|
||||||
can only service stdin and stdout. Lua errors are still sent to stderr which is
|
|
||||||
the UART0 device. Hence errors will fail silently. If you want to capture
|
|
||||||
errors then you will need to wrap any commands in a `pcall()` and print any
|
|
||||||
error return.
|
|
||||||
|
|
||||||
## telnet:open()
|
## telnet:open()
|
||||||
|
|
||||||
|
@ -64,7 +41,7 @@ Nothing returned (this is evaluted as `nil` in a scalar context).
|
||||||
|
|
||||||
## telnet:close()
|
## telnet:close()
|
||||||
|
|
||||||
Close a telnet server and release all resources.
|
Close a telnet server and release all resources. Also set the variable `telnet` to nil to fully reference and GC the resources.
|
||||||
|
|
||||||
#### Syntax
|
#### Syntax
|
||||||
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
-- a simple telnet server
|
|
||||||
|
|
||||||
telnet_srv = net.createServer(net.TCP, 180)
|
|
||||||
telnet_srv:listen(2323, function(socket)
|
|
||||||
local fifo = {}
|
|
||||||
local fifo_drained = true
|
|
||||||
|
|
||||||
local function sender(c)
|
|
||||||
if #fifo > 0 then
|
|
||||||
c:send(table.remove(fifo, 1))
|
|
||||||
else
|
|
||||||
fifo_drained = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function s_output(str)
|
|
||||||
table.insert(fifo, str)
|
|
||||||
if socket ~= nil and fifo_drained then
|
|
||||||
fifo_drained = false
|
|
||||||
sender(socket)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
node.output(s_output, 0) -- re-direct output to function s_ouput.
|
|
||||||
|
|
||||||
socket:on("receive", function(c, l)
|
|
||||||
node.input(l) -- works like pcall(loadstring(l)) but support multiple separate line
|
|
||||||
end)
|
|
||||||
socket:on("disconnection", function(c)
|
|
||||||
node.output(nil) -- un-regist the redirect output function, output goes to serial
|
|
||||||
end)
|
|
||||||
socket:on("sent", sender)
|
|
||||||
|
|
||||||
print("Welcome to NodeMCU world.")
|
|
||||||
end)
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
--[[ A telnet server T. Ellison, June 2019
|
||||||
|
|
||||||
|
This version of the telnet server demonstrates the use of the new stdin and stout
|
||||||
|
pipes, which is a C implementation of the Lua fifosock concept moved into the
|
||||||
|
Lua core. These two pipes are referenced in the Lua registry.
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
local modname = ...
|
||||||
|
local function telnet_session(socket)
|
||||||
|
local node = node
|
||||||
|
local stdout, sending
|
||||||
|
|
||||||
|
local function output_CB(opipe) -- upval: socket
|
||||||
|
stdout = opipe
|
||||||
|
local rec = opipe:read(1400)
|
||||||
|
if rec and #rec > 0 then socket:send(rec) end
|
||||||
|
return false -- don't repost as the on:sent will do this
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onsent_CB(skt) -- upval: stdout
|
||||||
|
local rec = stdout:read(1400)
|
||||||
|
if rec and #rec > 0 then skt:send(rec) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function disconnect_CB(skt) -- upval: socket, stdout
|
||||||
|
node.output()
|
||||||
|
socket, stdout = nil, nil -- set upvals to nl to allow GC
|
||||||
|
end
|
||||||
|
|
||||||
|
node.output(output_CB, 0)
|
||||||
|
socket:on("receive", function(_,rec) node.input(rec) end)
|
||||||
|
socket:on("sent", onsent_CB)
|
||||||
|
socket:on("disconnection", disconnect_CB)
|
||||||
|
print(("Welcome to NodeMCU world (%d mem free, %s)"):format(
|
||||||
|
node.heap(), wifi.sta.getip()))
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.open(this, ssid, pwd, port)
|
||||||
|
local tmr, wifi, uwrite = tmr, wifi, uart.write
|
||||||
|
if ssid then
|
||||||
|
wifi.setmode(wifi.STATION, false)
|
||||||
|
wifi.sta.config { ssid = ssid, pwd = pwd, save = false }
|
||||||
|
end
|
||||||
|
local t = tmr.create()
|
||||||
|
t:alarm(500, tmr.ALARM_AUTO, function()
|
||||||
|
if (wifi.sta.status() == wifi.STA_GOTIP) then
|
||||||
|
t:unregister()
|
||||||
|
t=nil
|
||||||
|
print(("Telnet server started (%d mem free, %s)"):format(
|
||||||
|
node.heap(), wifi.sta.getip()))
|
||||||
|
M.svr = net.createServer(net.TCP, 180)
|
||||||
|
M.svr:listen(port or 23, telnet_session)
|
||||||
|
else
|
||||||
|
uwrite(0,".")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.close(this)
|
||||||
|
if this.svr then this.svr:close() end
|
||||||
|
package.loaded[modname] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue