close() went straight to shutdown(SHUT_RDWR) + close(). Doing that while either
side still has unread data makes the stack send RST rather than FIN, and the
device's FromRadio stream means there is essentially always unread data.
Winsock discards data that was received but not yet read when an RST arrives, so
an admin message written moments earlier is thrown away before the device reads
it. Linux delivers the buffered bytes before reporting ECONNRESET, which is why
this only surfaced on Windows: every one-shot TCP write there (--set, --seturl,
--sendtext) reported success and did nothing.
writeConfig() does not wait for an ack, so close() runs microseconds after
sendall(). The device cannot compensate; it polls every 5ms and the RST beats its
next read.
Send FIN first with shutdown(SHUT_WR), which lets the device consume what we
wrote, wait briefly for the reader thread to drain and exit, then tear down
exactly as before. The full shutdown is kept so a reader still blocked in recv()
is unblocked for the join in StreamInterface.close(). The wait is bounded: the
device is not obliged to close just because we half-closed.
Fixes#957
* Moving to socket.sendall() is safer, as sendall will send the entire
buffer, while send() would return the number of bytes sent and
require being called multiple times if the buffer was full.
* On exceptions: reconnect to the server.
* On reconnection: make sure using a lock that there isn't a race
between the readers and the writers triggering a reconnect.