2019-02-16 13:51:40 +01:00
|
|
|
# fifosock Module
|
|
|
|
| Since | Origin / Contributor | Maintainer | Source |
|
|
|
|
| :----- | :-------------------- | :---------- | :------ |
|
|
|
|
| 2019-02-10 | [TerryE](https://github.com/TerryE) | [nwf](https://github.com/nwf) | [fifosock.lua](../../lua_modules/fifo/fifosock.lua) |
|
|
|
|
|
2019-02-17 19:32:16 +01:00
|
|
|
This module provides a moderately convenient, efficient wrapper around the
|
|
|
|
`net.socket` `send` method. It ensures in-order transmission while striving to
|
|
|
|
minimize memory footprint and packet count by coalescing queued strings. It
|
|
|
|
also serves as a detailed, worked example of the `fifo` module.
|
2019-02-16 13:51:40 +01:00
|
|
|
|
|
|
|
## Use
|
|
|
|
```lua
|
2019-02-17 19:32:16 +01:00
|
|
|
ssend = (require "fifosock").wrap(sock)
|
2019-02-16 13:51:40 +01:00
|
|
|
ssend("hello, ") ssend("world\n")
|
2019-02-17 19:32:16 +01:00
|
|
|
|
|
|
|
-- when finished
|
|
|
|
ssend = nil
|
|
|
|
sock:on("sent", nil)
|
2019-02-16 13:51:40 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
Once the `sock`et has been wrapped, one should use only the resulting `ssend`
|
|
|
|
function in lieu of `sock:send`, and one should not change the
|
2019-02-17 19:32:16 +01:00
|
|
|
`sock:on("sent")` callback for the duration of the connection.
|
|
|
|
|
|
|
|
Use of this module creates a circular reference through the Lua registry: the
|
|
|
|
socket points at the fifosock wrapper, which points back at the socket. As
|
|
|
|
such, it is vitally important to break this cycle when the socket has outlived
|
|
|
|
its use. **The usual garbage collection will not be able to reclaim abandoned
|
|
|
|
wrapped sockets**. The user of `fifosock` must, when disposing of the socket,
|
|
|
|
unwire the wrapper, by calling `sock:on("sent", nil)` and should drop all
|
|
|
|
references to `ssend`; a convenient place to do this is in the
|
|
|
|
`sock:on("disconnect")` callback.
|
|
|
|
|
|
|
|
## Advanced Use
|
2019-02-16 13:51:40 +01:00
|
|
|
|
|
|
|
In addition to passing strings representing part of the stream to be sent, it
|
|
|
|
is possible to pass the resulting `ssend` function *functions*. These
|
|
|
|
functions will be given no parameters, but should return two values:
|
|
|
|
|
|
|
|
- A string to be sent on the socket, or `nil` if no output is desired
|
|
|
|
- A replacement function, or `nil` if the function is to be dequeued. Functions
|
|
|
|
may, of course, offer themselves as their own replacement to stay at the front
|
|
|
|
of the queue.
|
|
|
|
|
|
|
|
This facility is useful for providing a replacement for the `sock:on("sent")`
|
|
|
|
callback channel. In the fragment below, "All sent" will be `print`ed only
|
|
|
|
when the entirety of "hello, world\n" has been successfully sent on the
|
|
|
|
`sock`et.
|
|
|
|
|
|
|
|
```lua
|
|
|
|
ssend("hello, ") ssend("world\n")
|
|
|
|
ssend(function() print("All sent") end) -- implicitly returns nil, nil
|
|
|
|
```
|
|
|
|
|
|
|
|
This facility is also useful for *generators* of the stream, roughly akin to
|
|
|
|
`sendfile`-like primitives in larger systems. Here, for example, we can stream
|
|
|
|
SPIFFS data across the network without ever holding a large amount in RAM.
|
|
|
|
|
|
|
|
```lua
|
|
|
|
local function sendfile(fn)
|
|
|
|
local offset = 0
|
|
|
|
local function send()
|
|
|
|
local f = file.open(fn, "r")
|
|
|
|
if f and f:seek("set", offset) then
|
|
|
|
r = f:read()
|
|
|
|
f:close()
|
|
|
|
if r then
|
|
|
|
offset = offset + #r
|
|
|
|
return r, send
|
|
|
|
end
|
|
|
|
end
|
|
|
|
-- implicitly returns nil, nil and falls out of the stream
|
|
|
|
end
|
|
|
|
return send, function() return offset end
|
|
|
|
end
|
|
|
|
|
|
|
|
local fn = "test"
|
|
|
|
ssend(("Sending file '%s'...\n"):format(fn))
|
|
|
|
dosf, getsent = sendfile(fn)
|
|
|
|
ssend(dosf)
|
|
|
|
ssend(("Sent %d bytes from '%s'\n"):format(getsent(), fn))
|
|
|
|
```
|