0
# WebSocket Communication
1
2
Real-time bidirectional communication with WebSocket support for live data and interactive applications. Provides Python interface to browser WebSocket API for real-time web applications.
3
4
## Capabilities
5
6
### WebSocket Connection
7
8
Establish and manage WebSocket connections for real-time communication.
9
10
```python { .api }
11
class WebSocket:
12
"""
13
WebSocket connection handler.
14
"""
15
16
def __init__(self, url: str, protocols: list[str] = None):
17
"""
18
Create WebSocket connection.
19
20
Args:
21
url: WebSocket server URL (ws:// or wss://)
22
protocols: Optional list of sub-protocols
23
"""
24
25
def send(self, data: str | bytes) -> None:
26
"""
27
Send data through WebSocket.
28
29
Args:
30
data: Data to send (string or binary)
31
"""
32
33
def close(self, code: int = 1000, reason: str = "") -> None:
34
"""
35
Close WebSocket connection.
36
37
Args:
38
code: Close code (1000 = normal closure)
39
reason: Close reason string
40
"""
41
42
def bind(self, event: str, callback: Callable) -> None:
43
"""
44
Bind event handler.
45
46
Args:
47
event: Event type ('open', 'message', 'close', 'error')
48
callback: Event handler function
49
"""
50
51
# Properties
52
readyState: int # Connection state (0=connecting, 1=open, 2=closing, 3=closed)
53
url: str # Connection URL
54
protocol: str # Selected sub-protocol
55
bufferedAmount: int # Bytes queued for transmission
56
57
# Check WebSocket support
58
supported: bool # True if WebSocket is available in browser
59
```
60
61
**Basic Usage:**
62
```python
63
from browser.websocket import WebSocket, supported
64
65
if not supported:
66
print("WebSocket not supported in this browser")
67
else:
68
# Create WebSocket connection
69
ws = WebSocket("ws://localhost:8080/chat")
70
71
def on_open(event):
72
print("WebSocket connection established")
73
ws.send("Hello Server!")
74
75
def on_message(event):
76
message = event.data
77
print(f"Received: {message}")
78
79
def on_close(event):
80
print(f"Connection closed: {event.code} - {event.reason}")
81
82
def on_error(event):
83
print("WebSocket error occurred")
84
85
# Bind event handlers
86
ws.bind('open', on_open)
87
ws.bind('message', on_message)
88
ws.bind('close', on_close)
89
ws.bind('error', on_error)
90
```
91
92
### Chat Application
93
94
Real-time chat application using WebSocket communication.
95
96
```python { .api }
97
class ChatClient:
98
"""
99
Real-time chat client using WebSocket.
100
"""
101
102
def __init__(self, server_url: str, username: str):
103
"""
104
Initialize chat client.
105
106
Args:
107
server_url: WebSocket server URL
108
username: User's display name
109
"""
110
111
def connect(self) -> None:
112
"""Connect to chat server."""
113
114
def disconnect(self) -> None:
115
"""Disconnect from chat server."""
116
117
def send_message(self, message: str) -> None:
118
"""Send chat message."""
119
120
def join_room(self, room: str) -> None:
121
"""Join chat room."""
122
123
def leave_room(self, room: str) -> None:
124
"""Leave chat room."""
125
```
126
127
**Usage:**
128
```python
129
from browser.websocket import WebSocket
130
from browser import document, bind
131
from browser.html import DIV, INPUT, BUTTON, UL, LI
132
import json
133
134
class ChatApp:
135
def __init__(self):
136
self.ws = None
137
self.username = "User"
138
self.setup_ui()
139
self.connect()
140
141
def setup_ui(self):
142
"""Create chat interface."""
143
# Chat container
144
self.chat_container = DIV(style={
145
'width': '400px',
146
'height': '500px',
147
'border': '1px solid #ccc',
148
'display': 'flex',
149
'flex-direction': 'column'
150
})
151
152
# Messages area
153
self.messages_area = DIV(style={
154
'flex': '1',
155
'overflow-y': 'auto',
156
'padding': '10px',
157
'background': '#f9f9f9'
158
})
159
160
# Input area
161
input_area = DIV(style={
162
'display': 'flex',
163
'padding': '10px',
164
'border-top': '1px solid #ccc'
165
})
166
167
self.message_input = INPUT(
168
placeholder="Type a message...",
169
style={'flex': '1', 'margin-right': '10px'}
170
)
171
172
send_button = BUTTON("Send")
173
174
# Assemble UI
175
input_area <= self.message_input
176
input_area <= send_button
177
self.chat_container <= self.messages_area
178
self.chat_container <= input_area
179
document.body <= self.chat_container
180
181
# Event handlers
182
@bind(send_button, 'click')
183
def handle_send(event):
184
self.send_message()
185
186
@bind(self.message_input, 'keypress')
187
def handle_keypress(event):
188
if event.key == 'Enter':
189
self.send_message()
190
191
def connect(self):
192
"""Connect to WebSocket server."""
193
self.ws = WebSocket("ws://localhost:8080/chat")
194
195
def on_open(event):
196
self.add_system_message("Connected to chat server")
197
# Send join message
198
self.ws.send(json.dumps({
199
'type': 'join',
200
'username': self.username
201
}))
202
203
def on_message(event):
204
try:
205
data = json.loads(event.data)
206
self.handle_server_message(data)
207
except:
208
self.add_system_message(f"Received: {event.data}")
209
210
def on_close(event):
211
self.add_system_message("Disconnected from server")
212
213
def on_error(event):
214
self.add_system_message("Connection error")
215
216
self.ws.bind('open', on_open)
217
self.ws.bind('message', on_message)
218
self.ws.bind('close', on_close)
219
self.ws.bind('error', on_error)
220
221
def send_message(self):
222
"""Send chat message."""
223
message = self.message_input.value.strip()
224
if message and self.ws and self.ws.readyState == 1:
225
self.ws.send(json.dumps({
226
'type': 'message',
227
'username': self.username,
228
'text': message
229
}))
230
self.message_input.value = ""
231
232
def handle_server_message(self, data):
233
"""Handle messages from server."""
234
message_type = data.get('type')
235
236
if message_type == 'message':
237
self.add_chat_message(data['username'], data['text'])
238
elif message_type == 'join':
239
self.add_system_message(f"{data['username']} joined the chat")
240
elif message_type == 'leave':
241
self.add_system_message(f"{data['username']} left the chat")
242
elif message_type == 'error':
243
self.add_system_message(f"Error: {data['message']}")
244
245
def add_chat_message(self, username, text):
246
"""Add chat message to display."""
247
message_div = DIV(style={'margin': '5px 0'})
248
249
username_span = DIV(
250
f"{username}: ",
251
style={'font-weight': 'bold', 'display': 'inline'}
252
)
253
254
text_span = DIV(
255
text,
256
style={'display': 'inline'}
257
)
258
259
message_div <= username_span
260
message_div <= text_span
261
self.messages_area <= message_div
262
263
# Scroll to bottom
264
self.messages_area.scrollTop = self.messages_area.scrollHeight
265
266
def add_system_message(self, text):
267
"""Add system message to display."""
268
message_div = DIV(
269
text,
270
style={
271
'margin': '5px 0',
272
'font-style': 'italic',
273
'color': '#666'
274
}
275
)
276
277
self.messages_area <= message_div
278
self.messages_area.scrollTop = self.messages_area.scrollHeight
279
280
# Start chat application
281
chat_app = ChatApp()
282
```
283
284
### Live Data Updates
285
286
Real-time data streaming for live updates and monitoring.
287
288
```python { .api }
289
class LiveDataClient:
290
"""
291
Client for receiving live data updates.
292
"""
293
294
def __init__(self, data_url: str):
295
"""
296
Initialize live data client.
297
298
Args:
299
data_url: WebSocket URL for data stream
300
"""
301
302
def subscribe(self, channel: str, callback: Callable) -> None:
303
"""
304
Subscribe to data channel.
305
306
Args:
307
channel: Data channel name
308
callback: Function to call with data updates
309
"""
310
311
def unsubscribe(self, channel: str) -> None:
312
"""Unsubscribe from data channel."""
313
```
314
315
**Usage:**
316
```python
317
from browser.websocket import WebSocket
318
from browser import document
319
from browser.html import DIV, TABLE, TR, TD, TH
320
import json
321
322
class StockTicker:
323
def __init__(self):
324
self.ws = None
325
self.stocks = {}
326
self.setup_ui()
327
self.connect()
328
329
def setup_ui(self):
330
"""Create stock ticker UI."""
331
self.container = DIV(style={
332
'width': '600px',
333
'margin': '20px auto'
334
})
335
336
title = DIV("Live Stock Prices", style={
337
'font-size': '24px',
338
'font-weight': 'bold',
339
'text-align': 'center',
340
'margin-bottom': '20px'
341
})
342
343
# Create table
344
self.table = TABLE(style={
345
'width': '100%',
346
'border-collapse': 'collapse'
347
})
348
349
# Table header
350
header = TR()
351
header <= TH("Symbol", style={'border': '1px solid #ccc', 'padding': '10px'})
352
header <= TH("Price", style={'border': '1px solid #ccc', 'padding': '10px'})
353
header <= TH("Change", style={'border': '1px solid #ccc', 'padding': '10px'})
354
header <= TH("Volume", style={'border': '1px solid #ccc', 'padding': '10px'})
355
356
self.table <= header
357
358
self.container <= title
359
self.container <= self.table
360
document.body <= self.container
361
362
def connect(self):
363
"""Connect to stock data WebSocket."""
364
self.ws = WebSocket("ws://localhost:8080/stocks")
365
366
def on_open(event):
367
print("Connected to stock data stream")
368
# Subscribe to stock updates
369
self.ws.send(json.dumps({
370
'action': 'subscribe',
371
'symbols': ['AAPL', 'GOOGL', 'MSFT', 'TSLA', 'AMZN']
372
}))
373
374
def on_message(event):
375
try:
376
data = json.loads(event.data)
377
if data['type'] == 'quote':
378
self.update_stock(data)
379
except Exception as e:
380
print(f"Error processing data: {e}")
381
382
def on_close(event):
383
print("Stock data stream closed")
384
385
self.ws.bind('open', on_open)
386
self.ws.bind('message', on_message)
387
self.ws.bind('close', on_close)
388
389
def update_stock(self, quote):
390
"""Update stock display with new quote."""
391
symbol = quote['symbol']
392
price = quote['price']
393
change = quote['change']
394
volume = quote['volume']
395
396
# Store quote
397
self.stocks[symbol] = quote
398
399
# Find or create table row
400
row_id = f"row_{symbol}"
401
row = document.get(row_id)
402
403
if not row:
404
# Create new row
405
row = TR(id=row_id)
406
407
symbol_cell = TD(symbol, style={'border': '1px solid #ccc', 'padding': '10px'})
408
price_cell = TD(f"${price:.2f}", style={'border': '1px solid #ccc', 'padding': '10px'})
409
410
# Color change cell based on positive/negative
411
change_color = 'green' if change >= 0 else 'red'
412
change_text = f"+{change:.2f}" if change >= 0 else f"{change:.2f}"
413
change_cell = TD(change_text, style={
414
'border': '1px solid #ccc',
415
'padding': '10px',
416
'color': change_color
417
})
418
419
volume_cell = TD(f"{volume:,}", style={'border': '1px solid #ccc', 'padding': '10px'})
420
421
row <= symbol_cell
422
row <= price_cell
423
row <= change_cell
424
row <= volume_cell
425
426
self.table <= row
427
else:
428
# Update existing row
429
cells = row.querySelectorAll('td')
430
cells[1].textContent = f"${price:.2f}"
431
432
change_color = 'green' if change >= 0 else 'red'
433
change_text = f"+{change:.2f}" if change >= 0 else f"{change:.2f}"
434
cells[2].textContent = change_text
435
cells[2].style.color = change_color
436
437
cells[3].textContent = f"{volume:,}"
438
439
# Start stock ticker
440
stock_ticker = StockTicker()
441
```
442
443
### Connection Management
444
445
Advanced WebSocket connection handling with reconnection and heartbeat.
446
447
```python { .api }
448
class ReliableWebSocket:
449
"""
450
WebSocket with automatic reconnection and heartbeat.
451
"""
452
453
def __init__(self, url: str, options: dict = None):
454
"""
455
Initialize reliable WebSocket.
456
457
Args:
458
url: WebSocket server URL
459
options: Connection options (reconnect_interval, max_retries, etc.)
460
"""
461
462
def set_heartbeat(self, interval: int, message: str = "ping") -> None:
463
"""
464
Enable heartbeat to keep connection alive.
465
466
Args:
467
interval: Heartbeat interval in milliseconds
468
message: Heartbeat message to send
469
"""
470
471
def enable_auto_reconnect(self, max_retries: int = -1,
472
backoff: bool = True) -> None:
473
"""
474
Enable automatic reconnection on disconnect.
475
476
Args:
477
max_retries: Maximum reconnection attempts (-1 for unlimited)
478
backoff: Use exponential backoff for retry delays
479
"""
480
```
481
482
**Usage:**
483
```python
484
from browser.websocket import WebSocket
485
from browser.timer import set_interval, clear_interval
486
487
class ReliableConnection:
488
def __init__(self, url):
489
self.url = url
490
self.ws = None
491
self.reconnect_attempts = 0
492
self.max_reconnect_attempts = 5
493
self.reconnect_interval = 1000 # Start with 1 second
494
self.heartbeat_interval = None
495
self.connect()
496
497
def connect(self):
498
"""Establish WebSocket connection."""
499
print(f"Connecting to {self.url}...")
500
self.ws = WebSocket(self.url)
501
502
def on_open(event):
503
print("Connected successfully")
504
self.reconnect_attempts = 0
505
self.reconnect_interval = 1000 # Reset interval
506
self.start_heartbeat()
507
508
def on_message(event):
509
if event.data == "pong":
510
print("Heartbeat response received")
511
else:
512
# Handle actual messages
513
print(f"Received: {event.data}")
514
515
def on_close(event):
516
print("Connection closed")
517
self.stop_heartbeat()
518
if event.code != 1000: # Not normal closure
519
self.attempt_reconnect()
520
521
def on_error(event):
522
print("Connection error")
523
524
self.ws.bind('open', on_open)
525
self.ws.bind('message', on_message)
526
self.ws.bind('close', on_close)
527
self.ws.bind('error', on_error)
528
529
def start_heartbeat(self):
530
"""Start sending heartbeat messages."""
531
def send_ping():
532
if self.ws and self.ws.readyState == 1:
533
self.ws.send("ping")
534
535
self.heartbeat_interval = set_interval(send_ping, 30000) # 30 seconds
536
537
def stop_heartbeat(self):
538
"""Stop heartbeat."""
539
if self.heartbeat_interval:
540
clear_interval(self.heartbeat_interval)
541
self.heartbeat_interval = None
542
543
def attempt_reconnect(self):
544
"""Attempt to reconnect with exponential backoff."""
545
if self.reconnect_attempts >= self.max_reconnect_attempts:
546
print("Max reconnection attempts reached")
547
return
548
549
self.reconnect_attempts += 1
550
delay = self.reconnect_interval * (2 ** (self.reconnect_attempts - 1))
551
552
print(f"Reconnecting in {delay}ms (attempt {self.reconnect_attempts})")
553
554
def do_reconnect():
555
self.connect()
556
557
set_timeout(do_reconnect, delay)
558
559
def send(self, data):
560
"""Send data if connected."""
561
if self.ws and self.ws.readyState == 1:
562
self.ws.send(data)
563
else:
564
print("Cannot send - not connected")
565
566
def close(self):
567
"""Close connection."""
568
self.stop_heartbeat()
569
if self.ws:
570
self.ws.close(1000, "Client closing")
571
572
# Usage
573
reliable_ws = ReliableConnection("ws://localhost:8080/api")
574
```
575
576
This WebSocket system provides comprehensive real-time communication capabilities for Brython applications, enabling live chat, data streaming, notifications, and other interactive features that require bidirectional communication between client and server.