1 class BrokenSocketConnection(Exception):
8 def __init__(self, socket):
11 def send(self, message, silent_connection_break=False):
12 """Send via self.socket, encoded/delimited as way recv() expects.
14 In detail, all \ and $ in message are escaped with prefixed \,
15 and an unescaped $ is appended as a message delimiter. Then,
16 socket.send() is called as often as necessary to ensure
17 message is sent fully, as socket.send() due to buffering may
18 not send all of it right away.
20 Assuming socket is blocking, it's rather improbable that
21 socket.send() will be partial / return a positive value less
22 than the (byte) length of msg – but not entirely out of the
23 question. See: - <http://stackoverflow.com/q/19697218> -
24 <http://stackoverflow.com/q/2618736> -
25 <http://stackoverflow.com/q/8900474>
27 This also handles a socket.send() return value of 0, which
28 might be possible or not (?) for blocking sockets: -
29 <http://stackoverflow.com/q/34919846>
34 if char in ('\\', '$'):
35 escaped_message += '\\'
36 escaped_message += char
37 escaped_message += '$'
38 data = escaped_message.encode()
40 while totalsent < len(data):
43 sent = self.socket.send(data[totalsent:])
44 socket_broken = sent == 0
45 except OSError as err:
46 if err.errno == 9: # "Bad file descriptor", when connection broken
50 if socket_broken and not silent_connection_break:
51 raise BrokenSocketConnection
52 totalsent = totalsent + sent
55 """Get full send()-prepared message from self.socket.
57 In detail, socket.recv() is looped over for sequences of bytes
58 that can be decoded as a Unicode string delimited by an
59 unescaped $, with \ and $ escapable by \. If a sequence of
60 characters that ends in an unescaped $ cannot be decoded as
61 Unicode, None is returned as its representation. Stop once
62 socket.recv() returns nothing.
64 Under the hood, the TCP stack receives packets that construct
65 the input payload in an internal buffer; socket.recv(BUFSIZE)
66 pops up to BUFSIZE bytes from that buffer, without knowledge
67 either about the input's segmentation into packets, or whether
68 the input is segmented in any other meaningful way; that's why
69 we do our own message segmentation with $ as a delimiter.
76 data += self.socket.recv(1024)
90 except UnicodeDecodeError: