1
# -*- coding: UTF-8 -*-
10
Format of Transported Data
11
--------------------------
13
In order to communicate with external non-python programs, the xdr module
14
implements a very simple protocol à la XDR.
16
The send function expect variable length arguments of str type.
17
The arguments will be escaped, joint and packed into binary.
18
All the arguments have to be joint into a '\\n' delimited str.
20
e.g. send(s, "a", "b", "c") -> "a\\nb\\nc"
22
If one argument contains '\n' it has to be escaped
24
e.g. send(s, "a\\n", "b", "c") -> "a\\\\n\\nb\\nc"
26
If their are no arguments, an empty str is sent.
28
To be safely and efficiently exchanged, arguments are packed into the
29
following representation :
32
+-----+-----+-----+-----+-----+---+
33
| length n |byte0|byte1|...| n-1 |
34
+-----+-----+-----+-----+-----+---+
35
|<-2 bytes->|<------n bytes------>|
38
The length is encoded into a two bytes unsigned integer in native
41
e.g. send(s, "a", "b", "c") -> "\\x05\\x00a\\nb\\nc" (05 00 61 0a 62 0a 63)
43
Recieved data has to be unpacked, splitted and unescaped.
46
# we should have used xrdlib
47
# but i don't know if this add another dependency
48
# and a Packer object has to be created each call
50
# if we get issues with the current protocol, we should
56
_SIZEOF_HEADER = struct.calcsize(_HEADER_FORMAT)
59
# 16 bits unsigned integer header in native order representif the length of
60
# the following string. The string represents a '\n'-seperated tuple.
2
This module implements a simple tuple transmission protocol. It's kept simple
3
to be implementable in other languages as well.
5
The protocol can only transmit lists of strings, which it splits up into chunks.
6
A chunk consists of a LENGTH byte, the actual PAYLOAD, and a STATUS byte.
8
A single string is split up into several chunks if it exceeds the maximum length
9
which can be specified by the LENGTH byte (255), otherwise a string is one
12
The STATUS byte after every chunk tells if the chunk is
14
- continued in the next chunk (CONT)
15
- the last chunk of a string (NEXT)
16
- the last chunk in the transmission (END)
18
In order to handle empty lists without getting too complicated, all lists are
19
extended by an arbitrary first element which just gets ignored.
24
["Hello", "World!"] is transmitted as:
26
01 00 01 05 48 65 6C 6C 6F 01 06 57 6F 72 6C 64 21 02
28
(1) ? NEXT (5) H e l l o NEXT (6) W o r l d ! END
32
class XDRError(RuntimeError):
36
_SEND_ERROR = "--SEND ERROR--"
63
44
def send(s, *args):
66
Writes data on a socket
68
@param s: given socket
69
@type s: socket object
71
@param *args: variable length arguments
74
NB: *args is a tuple and therefor a tuple is transmitted on the socket.
75
That's why recv returns a tuple equals to args.
78
data = '\n'.join([ str(a).replace('\n', '\\n') for a in args ])
80
header = struct.pack(_HEADER_FORMAT, len(data))
46
args = ["\0"] + list(args)
50
chunks = [ a[i:i + 0xff] for i in range(0, len(a), 0xff) ]
55
if (chunks): s.send(_CONT)
57
if (args): s.send(_NEXT)
90
Reads data on a socket
92
@param s: given socket
93
@type s: socket object
95
@return : incoming data
99
header = s.recv(_SIZEOF_HEADER)
104
size = struct.unpack(_HEADER_FORMAT, header)[0]
107
return tuple([ a.replace('\\n', '\n') for a in data.split('\n') ])
73
length = ord(s.recv(1))
77
if (length): chunk += s.recv(length)
80
if (flag == _CONT): continue
85
if (flag == _END): break