<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Steve,<div class=""><br class=""></div><div class="">The general approach of async net libraries like mongoose seem to be that they don’t allow blocking, and that is why the send doesn’t send immediately. That said, I’ve seen implementations that at least _try_ to send immediately (and only queue it if that fails); however that can complicate the transmitter (which has to deal with the two possible cases).</div><div class=""><br class=""></div><div class="">The more general approach seems to be that if we want to send something big, we send a chunk, than wait for the MG_EV_SEND message to indicate that chunk has been sent, then queue the next chunk to go. That seems to work well in Mongoose. My issue is that the higher-level protocols in Mongoose don’t support that well (for example, HTTP client and server). Kind of disappointing. But, the basic low-level networking API seems ok.</div><div class=""><br class=""></div><div class="">I can see how something like ‘vfs cat my-big-file’ would cause us concern.</div><div class=""><br class=""></div><div class="">I tried the telnet server component, with wifi up, and that ESP_LOGI on to log the event. Dropping the event #0 (poll) notifications, this is what I saw:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><font face="Andale Mono" class="">I (22689) telnet: Launching Telnet Server</font></div><div class=""><font face="Andale Mono" class=""><br class=""></font></div><div class=""><font face="Andale Mono" class="">(Launch a normal telnet connection)</font></div><div class=""><div class=""><font face="Andale Mono" class="">I (47049) telnet: Event 1 conn 0x3fff1004, data 0x3fff101c (MG_EV_ACCEPT)</font></div><div class=""><font face="Andale Mono" class="">I (47049) telnet: Event 3 conn 0x3fff1004, data 0x3ffeffb0 (MG_EV_RECV)</font></div><div class=""><font face="Andale Mono" class="">I (47099) telnet: Event 4 conn 0x3fff1004, data 0x3ffeffc0 (MG_EV_SEND)</font></div><div class=""><font face="Andale Mono" class="">I (47109) telnet: Event 3 conn 0x3fff1004, data 0x3ffeffb0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_RECV)</span></div><div class=""><font face="Andale Mono" class="">I (51749) telnet: Event 3 conn 0x3fff1004, data 0x3ffeffb0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_RECV)</span></div><div class=""><font face="Andale Mono" class="">I (51759) telnet: Event 4 conn 0x3fff1004, data 0x3ffeffc0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_SEND)</span></div><div class=""><font face="Andale Mono" class="">I (51889) telnet: Event 3 conn 0x3fff1004, data 0x3ffeffb0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_RECV)</span></div><div class=""><font face="Andale Mono" class="">I (51899) telnet: Event 4 conn 0x3fff1004, data 0x3ffeffc0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_SEND)</span></div><div class=""><font face="Andale Mono" class="">I (51939) telnet: Event 3 conn 0x3fff1004, data 0x3ffeffb0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_RECV)</span></div><div class=""><font face="Andale Mono" class="">I (51949) telnet: Event 4 conn 0x3fff1004, data 0x3ffeffc0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_SEND)</span></div><div class=""><font face="Andale Mono" class="">I (52049) telnet: Event 3 conn 0x3fff1004, data 0x3ffeffb0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_RECV)</span></div><div class=""><font face="Andale Mono" class="">I (52059) telnet: Event 4 conn 0x3fff1004, data 0x3ffeffc0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_SEND)</span></div><div class=""><font face="Andale Mono" class="">I (52209) telnet: Event 3 conn 0x3fff1004, data 0x3ffeffb0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_RECV)</span></div><div class=""><font face="Andale Mono" class="">I (52209) telnet: Event 4 conn 0x3fff1004, data 0x3ffeffc0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_SEND)</span></div><div class=""><span style="font-family: 'Andale Mono';" class="">(Disconnect the client side)</span></div><div class=""><font face="Andale Mono" class="">I (54309) telnet: Event 5 conn 0x3fff1004, data 0x0       </font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_CLOSE)</span></div><div class=""><span style="font-family: 'Andale Mono';" class=""><br class=""></span></div><div class=""><span style="font-family: 'Andale Mono';" class="">(Launch a normal telnet connection)</span></div><div class=""><font face="Andale Mono" class="">I (58319) telnet: Event 1 conn 0x3fff0f08, data 0x3fff0f20 (MG_EV_ACCEPT)</font></div><div class=""><font face="Andale Mono" class="">I (58319) telnet: Event 3 conn 0x3fff0f08, data 0x3ffeffb0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_RECV)</span></div><div class=""><font face="Andale Mono" class="">I (58369) telnet: Event 4 conn 0x3fff0f08, data 0x3ffeffc0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_SEND)</span></div><div class=""><font face="Andale Mono" class="">I (58379) telnet: Event 3 conn 0x3fff0f08, data 0x3ffeffb0</font><span style="font-family: 'Andale Mono';" class=""> </span><span style="font-family: 'Andale Mono';" class="">(MG_EV_RECV)</span></div><div class=""><span style="font-family: 'Andale Mono';" class=""><br class=""></span></div><div class=""><font face="Andale Mono" class="">OVMS > wifi mode off</font></div><div class=""><font face="Andale Mono" class="">Stopping wifi station...</font></div><div class=""><font face="Andale Mono" class="">I (62569) events: Signal(system.wifi.down)</font></div><div class=""><font face="Andale Mono" class="">I (62569) events: Signal(network.wifi.down)</font></div><div class=""><font face="Andale Mono" class="">I (62569) ssh: Stopping SSH Server</font></div><div class=""><font face="Andale Mono" class="">I (62579) events: Signal(network.down)</font></div><div class=""><font face="Andale Mono" class="">I (62579) wifi: state: run -> init (0)</font></div><div class=""><font face="Andale Mono" class="">I (62579) wifi: pm stop, total sleep time: 0/33441266</font></div><div class=""><font face="Andale Mono" class=""><br class=""></font></div><div class=""><font face="Andale Mono" class="">I (62579) wifi: n:11 0, o:11 0, ap:255 255, sta:11 0, prof:1</font></div><div class=""><font face="Andale Mono" class="">I (62589) events: Signal(system.wifi.sta.disconnected)</font></div><div class=""><font face="Andale Mono" class=""><br class=""></font></div><div class=""><font face="Andale Mono" class="">I (62589) telnet: Event 5 conn 0x3fff0f08, data 0x0 </font><span style="font-family: 'Andale Mono';" class="">(MG_EV_CLOSE)</span></div><div class=""><font face="Andale Mono" class=""><br class=""></font></div><div class=""><font face="Andale Mono" class="">I (62589) events: Signal(network.mgr.stop)</font></div><div class=""><font face="Andale Mono" class="">I (62589) telnet: Stopping Telnet Server</font></div><div class=""><font face="Andale Mono" class="">I (62589) events: Signal(system.wifi.sta.stop)</font></div><div class=""><font face="Andale Mono" class="">I (62589) telnet: Event 5 conn 0x3fff0ccc, data 0x0 (MG_EV_CLOSE)</font></div></div></blockquote><div class=""><br class=""></div><div class="">As you say, not elegant, but at least our side gets closed.</div><div class=""><br class=""></div><div class="">Regards, Mark.</div><div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On 20 Nov 2017, at 8:58 AM, Stephen Casner <<a href="mailto:casner@acm.org" class="">casner@acm.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">On Sun, 19 Nov 2017, Mark Webb-Johnson wrote:<br class=""><blockquote type="cite" class="">Strange. When I tested mongoose, I got a close event (MG_EV_CLOSE) for<br class="">each open connection, when the interface went down.<br class=""></blockquote><br class="">It does send a close event, but not until after the wifi is already<br class="">shut down so closing the socket at that point does not send any packet<br class="">to the client.  I've decided to punt on that issue, though, because<br class="">manually shutting down wifi is not an important use case.  The more<br class="">likely case is that wifi connectivity is lost due to motion or other<br class="">causes and in that case no close packet can be delivered anyway.<br class=""><br class="">My other points were:<br class=""><br class=""><blockquote type="cite" class=""><blockquote type="cite" class=""> - It looks like there may be a memory blocked leaked at each client<br class="">   disconnection.  I need to find that.<br class=""></blockquote></blockquote><br class="">This is not a leak.  It is the policy of lwip to allocate a semaphore<br class="">to each socket and to not reuse socket structures in its pool until<br class="">all have been used.  So that means up to 10 semaphore blocks of 92<br class="">bytes each (104 with debug overhead) will be allocated as new client<br class="">connections are made.  I actually figured this out once before in<br class="">August when I first implemented telnet.<br class=""><br class=""><blockquote type="cite" class=""><blockquote type="cite" class=""> - I think I can improve how I handle the receive buffer and save<br class="">   some more RAM.<br class=""><br class="">I've also thought more about how to convert the SSH Console and will<br class="">take a stab at that as well.<br class=""></blockquote></blockquote><br class="">In my local repository I have done both of these improvements but not<br class="">yet committed them.<br class=""><br class="">Furthermore, I have determined that the "surgery" in OvmsConsole<br class="">required to accommodate ConsoleAsync as a task while ConsoleTelnet and<br class="">ConsoleSSH run within the NetManTask was not as bad as I thought.  So,<br class="">as you requested, in my local copy I have eliminated all the dedicated<br class="">tasks for the Telnet and SSH implementations and have that working.<br class=""><br class="">However, there are a couple of downsides to using mongoose:<br class=""><br class="">1) Output from other tasks through the OvmsCommandApp::Log() facility<br class="">will be delayed on average by half the mongoose polling interval,<br class="">which is currently one second.  In my original implementation I<br class="">avoided polling to avoid delays, but that had other costs.<br class=""><br class="">2) More serious: Mongoose has a policy that callback function will not<br class="">block, which is reasonable since it acts as a hub for multiple<br class="">services.  However, one consequence is that the mg_send() function<br class="">always just buffers the data you give it into heap RAM.  So, in the<br class="">SSH console, for example, when you enter a command an MG_EV_RECV event<br class="">will pass control into code in the console and command classes.  That<br class="">code will generate its output as multiple puts or printf calls each of<br class="">which results in an mg_send() call.  For a command like "?" that does<br class="">many lines of output, mongoose will be doing a bunch of realloc()<br class="">operations to keep appending all of the lines into one big buffer in<br class="">RAM.  It is possible for a command to use up all the available RAM.<br class="">Fortunately mg_send() fails gracefully when that happens, but it means<br class="">command output is lost and in the meantime other functions that depend<br class="">upon RAM may fail less gracefully.  It was issues like this that led<br class="">me to be concerned about eliminating the console tasks.<br class=""><br class="">There is not any mechanism in Mongoose for the application to flush<br class="">the send buffer.  The data will only be sent when the callback for the<br class="">MG_EV_RECV event returns to Mongoose.  We don't have a way to return<br class="">to mongoose part way through the command output and then get control<br class="">back again to finish the command.  The state of being in the middle of<br class="">the command is lost without it being a separate task to hold that<br class="">state.  It might work to recurse into mongoose by calling<br class="">mg_mgr_poll() again, but the code in mongoose may not be reenterable<br class="">and this would incur significant risk of stack overflow.<br class=""><br class="">Should we consider modifying Mongoose to change its policy so that<br class="">mg_send() actually sends its data?  This could be a flag in the<br class="">mg_connection object, or there could just be an mg_flush() call<br class="">added.  Digging through the code I found mg_mgr_handle_conn() that<br class="">does the actual sending, but that function is not exposed in the<br class="">header file and I don't know whether it would be valid to call it from<br class="">user code while in the middle of the callback.<br class=""><br class="">You also said:<br class=""><br class=""><blockquote type="cite" class=""><blockquote type="cite" class=""><blockquote type="cite" class=""><blockquote type="cite" class="">Perhaps a generic extension to ovms_netmanager where you give it a<br class="">tcp port and an object type. Then, it listens on that port and<br class="">launches objects of that type (pre-configured to receive mongoose<br class="">events as appropriate) for each new connection? Have a base virtual<br class="">connection class, that gets inherited by each implementation (ssh,<br class="">telnet, etc).<br class=""></blockquote></blockquote></blockquote></blockquote><br class="">Right now I just have some similar code in OvmsTelnet and OvmsSSH<br class="">classes.  When this settles down we could consider factoring that int<br class="">a base virtual connection class.<br class=""><br class="">                                                        -- Steve<br class="">_______________________________________________<br class="">OvmsDev mailing list<br class=""><a href="mailto:OvmsDev@lists.teslaclub.hk" class="">OvmsDev@lists.teslaclub.hk</a><br class="">http://lists.teslaclub.hk/mailman/listinfo/ovmsdev<br class=""></div></div></blockquote></div><br class=""></div></body></html>