Lightwight socket IO wrapper
Cameron Simpson
cs at zip.com.au
Mon Sep 21 02:27:13 EDT 2015
On 21Sep2015 12:40, Chris Angelico <rosuav at gmail.com> wrote:
>On Mon, Sep 21, 2015 at 11:55 AM, Cameron Simpson <cs at zip.com.au> wrote:
>> On 21Sep2015 10:34, Chris Angelico <rosuav at gmail.com> wrote:
>>> If you're going to add sequencing and acknowledgements to UDP,
>>> wouldn't it be easier to use TCP and simply prefix every message with
>>> a two-byte length?
>>
>> Frankly, often yes. That's what I do. (different length encoding, but
>> otherwise...)
>
>Out of interest, what encoding?
NB: this is for binary protocols.
I don't like embedding arbitrary size limits in protocols or data formats if I
can easily avoid it. So (for my home grown binary protocols) I encode unsigned
integers as big endian octets with the top bit meaning "another octet follows"
and the bottom 7 bits going to the value. So my packets look like:
encoded(length)data
For sizes below 128, one byte of length. For sizes 128-16383, two bytes. And so
on. Compact yet unbounded.
My new protocols ar probably going to derive from the scheme implemented in
the code cited below. "New" means as of some weeks ago, when I completely
rewrote a painful ad hoc protocol of mine and pulled out the general features
into what follows.
The actual packet format is implemented by the Packet class at the bottom of
this:
https://bitbucket.org/cameron_simpson/css/src/tip/lib/python/cs/serialise.py
Simple and flexible.
As for using that data format multiplexed with multiple channels, see the
PacketConnection class here:
https://bitbucket.org/cameron_simpson/css/src/tip/lib/python/cs/stream.py
Broadly, the packets are length[tag,flags[,channel#],payload] and one
implements whatever semantics one needs on top of that.
You can see this exercised over UNIX pipes and TCP streams in the unit tests
here:
https://bitbucket.org/cameron_simpson/css/src/tip/lib/python/cs/stream_tests.py
On the subject of packet stuffing, my preferred loop for that is visible in the
PacketConnection._send worker thread method, which goes:
fp = self._send_fp
Q = self._sendQ
for P in Q:
sig = (P.channel, P.tag, P.is_request)
if sig in self.__sent:
raise RuntimeError("second send of %s" % (P,))
self.__sent.add(sig)
write_Packet(fp, P)
if Q.empty():
fp.flush()
fp.close()
In short: get packets from the queue and write them to the stream buffer. If
the queue gets empty, _only then_ flush the buffer. This assures synchronicity
in comms while giving the IO library a chance to fill a buffer with several
packets.
Cheers,
Cameron Simpson <cs at zip.com.au>
ERROR 155 - You can't do that. - Data General S200 Fortran error code list
More information about the Python-list
mailing list