0
# Network Socket Extensions
1
2
Socket patching capabilities that extend Python socket objects with atomic BSON transmission methods. This enables seamless BSON communication over network connections with automatic serialization and deserialization.
3
4
## Capabilities
5
6
### Socket Patching
7
8
Patches the Python socket class to add BSON object transmission methods, enabling atomic send and receive operations for BSON data.
9
10
```python { .api }
11
def patch_socket():
12
"""
13
Patch the Python socket class with BSON transmission methods.
14
15
Adds the following methods to socket objects:
16
- recvbytes(bytes_needed, sock_buf=None): Read exact number of bytes
17
- recvobj(): Read complete BSON object
18
- sendobj(obj): Send BSON object atomically
19
20
Note: Must be called before creating socket objects to enable BSON methods
21
"""
22
```
23
24
Usage example:
25
26
```python
27
import socket
28
import bson
29
30
# Patch socket class first
31
bson.patch_socket()
32
33
# Create socket with BSON capabilities
34
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
35
36
# Now socket has BSON methods available
37
# sock.sendobj({"message": "hello"})
38
# obj = sock.recvobj()
39
```
40
41
### Atomic Byte Reception
42
43
Receive an exact number of bytes from a socket atomically, handling partial reads and connection closures gracefully.
44
45
```python { .api }
46
def recvbytes(self, bytes_needed, sock_buf=None):
47
"""
48
Atomically read exact number of bytes from socket.
49
50
This method is added to socket objects by patch_socket().
51
52
Parameters:
53
- bytes_needed: int, exact number of bytes to read
54
- sock_buf: BytesIO buffer to append to (optional, creates new if None)
55
56
Returns:
57
BytesIO: Buffer containing exactly bytes_needed bytes, or None if socket closed
58
59
Raises:
60
socket.error: On network errors
61
"""
62
```
63
64
Usage example:
65
66
```python
67
import socket
68
import bson
69
from io import BytesIO
70
71
bson.patch_socket()
72
73
# Server side example
74
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
75
server_sock.bind(('localhost', 8080))
76
server_sock.listen(1)
77
78
conn, addr = server_sock.accept()
79
80
# Read exactly 100 bytes
81
data_buffer = conn.recvbytes(100)
82
if data_buffer is not None:
83
raw_data = data_buffer.getvalue()
84
print(f"Received {len(raw_data)} bytes")
85
else:
86
print("Connection closed by client")
87
88
# Reuse buffer for multiple reads
89
buffer = BytesIO()
90
header = conn.recvbytes(4, buffer) # Read 4-byte header
91
if header is not None:
92
payload = conn.recvbytes(96, header) # Read 96 more bytes into same buffer
93
if payload is not None:
94
total_data = payload.getvalue() # Now contains 100 bytes total
95
```
96
97
### BSON Object Reception
98
99
Receive complete BSON objects from socket, automatically handling message framing and deserialization.
100
101
```python { .api }
102
def recvobj(self):
103
"""
104
Atomically receive a complete BSON object from socket.
105
106
This method is added to socket objects by patch_socket().
107
Handles BSON message framing automatically by reading length header first.
108
109
Returns:
110
dict: Deserialized BSON object, or None if socket closed
111
112
Raises:
113
socket.error: On network errors
114
ValueError: If BSON data is malformed
115
"""
116
```
117
118
Usage example:
119
120
```python
121
import socket
122
import bson
123
124
bson.patch_socket()
125
126
# Server receiving BSON objects
127
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
128
server_sock.bind(('localhost', 8080))
129
server_sock.listen(1)
130
131
conn, addr = server_sock.accept()
132
133
while True:
134
# Receive BSON object
135
obj = conn.recvobj()
136
137
if obj is None:
138
print("Client disconnected")
139
break
140
141
print(f"Received: {obj}")
142
143
# Process the received object
144
if obj.get('command') == 'quit':
145
break
146
147
conn.close()
148
```
149
150
### BSON Object Transmission
151
152
Send Python objects as BSON over socket connections with automatic serialization and framing.
153
154
```python { .api }
155
def sendobj(self, obj):
156
"""
157
Atomically send a BSON object over socket.
158
159
This method is added to socket objects by patch_socket().
160
Automatically serializes object to BSON and sends with proper framing.
161
162
Parameters:
163
- obj: dict or BSONCoding object to send
164
165
Raises:
166
socket.error: On network errors
167
UnknownSerializerError: If object cannot be serialized
168
"""
169
```
170
171
Usage example:
172
173
```python
174
import socket
175
import bson
176
177
bson.patch_socket()
178
179
# Client sending BSON objects
180
client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
181
client_sock.connect(('localhost', 8080))
182
183
# Send various BSON objects
184
messages = [
185
{"type": "greeting", "message": "Hello server!"},
186
{"type": "data", "values": [1, 2, 3, 4, 5]},
187
{"type": "user", "name": "Alice", "age": 30},
188
{"command": "quit"}
189
]
190
191
for msg in messages:
192
client_sock.sendobj(msg)
193
print(f"Sent: {msg}")
194
195
client_sock.close()
196
```
197
198
## Complete Client-Server Example
199
200
```python
201
# Server (server.py)
202
import socket
203
import threading
204
import bson
205
206
bson.patch_socket()
207
208
def handle_client(conn, addr):
209
print(f"Connected by {addr}")
210
211
while True:
212
try:
213
# Receive BSON object
214
obj = conn.recvobj()
215
216
if obj is None:
217
print(f"Client {addr} disconnected")
218
break
219
220
print(f"Received from {addr}: {obj}")
221
222
# Echo back with server timestamp
223
import time
224
response = {
225
"echo": obj,
226
"server_time": time.time(),
227
"status": "received"
228
}
229
conn.sendobj(response)
230
231
except Exception as e:
232
print(f"Error handling {addr}: {e}")
233
break
234
235
conn.close()
236
237
# Start server
238
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
239
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
240
server.bind(('localhost', 8080))
241
server.listen(5)
242
print("Server listening on port 8080")
243
244
while True:
245
conn, addr = server.accept()
246
thread = threading.Thread(target=handle_client, args=(conn, addr))
247
thread.start()
248
```
249
250
```python
251
# Client (client.py)
252
import socket
253
import bson
254
import time
255
256
bson.patch_socket()
257
258
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
259
client.connect(('localhost', 8080))
260
261
# Send test messages
262
test_data = [
263
{"message": "Hello", "id": 1},
264
{"data": [10, 20, 30], "type": "array"},
265
{"user": {"name": "Bob", "role": "admin"}, "nested": True}
266
]
267
268
for data in test_data:
269
# Send object
270
client.sendobj(data)
271
272
# Receive response
273
response = client.recvobj()
274
if response:
275
print(f"Server response: {response}")
276
277
time.sleep(1)
278
279
client.close()
280
```
281
282
## Error Handling
283
284
### Network Errors
285
286
All socket methods can raise standard socket exceptions:
287
- `socket.error`: General network errors
288
- `ConnectionResetError`: Connection closed by peer
289
- `ConnectionAbortedError`: Connection aborted
290
- `TimeoutError`: Socket timeout
291
292
### BSON Errors
293
294
BSON-specific errors during network operations:
295
- `UnknownSerializerError`: Cannot serialize object in `sendobj()`
296
- `ValueError`: Malformed BSON data in `recvobj()`
297
298
### Connection Closure Detection
299
300
Methods return `None` when the remote end closes the connection cleanly:
301
302
```python
303
obj = sock.recvobj()
304
if obj is None:
305
# Connection closed cleanly
306
print("Peer disconnected")
307
else:
308
# Process received object
309
handle_message(obj)
310
```
311
312
## Implementation Notes
313
314
- `recvbytes()` reads data in chunks up to 32KB to handle large messages efficiently
315
- `recvobj()` first reads the 4-byte BSON length header, then the remaining message
316
- `sendobj()` uses `socket.sendall()` to ensure complete transmission
317
- All methods handle both Python 2 and 3 byte string differences transparently
318
- Socket patching is global and affects all socket instances created after patching