or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-types.mdindex.mdmessage-handling.mdservers.mdtcp-networking.mdudp-networking.md

servers.mddocs/

0

# Server Implementations

1

2

Complete OSC server implementations supporting UDP and TCP protocols with multiple concurrency models (blocking, threading, forking, asyncio), flexible message dispatching, and comprehensive handler management for building robust OSC services.

3

4

## Capabilities

5

6

### Message Dispatching

7

8

Core dispatcher system for routing OSC messages to handler functions based on address patterns.

9

10

```python { .api }

11

class Dispatcher:

12

"""Maps OSC addresses to handler functions and dispatches messages."""

13

14

def __init__(self):

15

"""Initialize empty dispatcher."""

16

17

def map(self, address: str, handler: Callable, *args, needs_reply_address: bool = False):

18

"""Map OSC address pattern to handler function.

19

20

Parameters:

21

- address: OSC address pattern (supports wildcards like /synth/*)

22

- handler: Function to call when address matches

23

- args: Fixed arguments to pass to handler before OSC arguments

24

- needs_reply_address: Whether to pass client address to handler

25

"""

26

27

def unmap(self, address: str, handler: Callable, *args, needs_reply_address: bool = False):

28

"""Remove mapped handler from address pattern.

29

30

Parameters:

31

- address: OSC address pattern to unmap

32

- handler: Handler function to remove

33

- args: Fixed arguments that were mapped

34

- needs_reply_address: Must match original mapping

35

"""

36

37

def handlers_for_address(self, address_pattern: str) -> Generator[Handler, None, None]:

38

"""Get handlers matching address pattern.

39

40

Parameters:

41

- address_pattern: OSC address to match against

42

43

Yields:

44

Handler objects that match the address pattern

45

"""

46

47

def call_handlers_for_packet(self, data: bytes, client_address: Tuple[str, int]) -> List:

48

"""Invoke handlers for all messages in OSC packet.

49

50

Parameters:

51

- data: Raw OSC packet datagram

52

- client_address: Client IP address and port tuple

53

54

Returns:

55

List of handler return values for response messages

56

"""

57

58

async def async_call_handlers_for_packet(self, data: bytes, client_address: Tuple[str, int]) -> List:

59

"""Asynchronously invoke handlers for all messages in OSC packet.

60

61

Parameters:

62

- data: Raw OSC packet datagram

63

- client_address: Client IP address and port tuple

64

65

Returns:

66

List of handler return values for response messages

67

"""

68

69

def set_default_handler(self, handler: Callable, needs_reply_address: bool = False):

70

"""Set default handler for unmatched addresses.

71

72

Parameters:

73

- handler: Function to call for unmatched addresses

74

- needs_reply_address: Whether to pass client address to handler

75

"""

76

77

class Handler:

78

"""Wrapper for callback functions mapped to OSC addresses."""

79

80

def __init__(self, callback: Callable, args: Union[Any, List[Any]],

81

needs_reply_address: bool = False):

82

"""Initialize handler wrapper.

83

84

Parameters:

85

- callback: Function to call when invoked

86

- args: Fixed arguments to pass to callback

87

- needs_reply_address: Whether to pass client address

88

"""

89

90

def invoke(self, client_address: Tuple[str, int], message: OscMessage) -> Union[None, str, Tuple]:

91

"""Invoke the handler callback.

92

93

Parameters:

94

- client_address: Client IP and port

95

- message: OSC message that triggered invocation

96

97

Returns:

98

None, OSC address string, or (address, args) tuple for responses

99

"""

100

```

101

102

### UDP Servers

103

104

UDP server implementations with multiple concurrency models for different performance requirements.

105

106

```python { .api }

107

class OSCUDPServer:

108

"""Base UDP server class."""

109

110

def __init__(self, server_address: Tuple[str, int], dispatcher: Dispatcher,

111

bind_and_activate: bool = True):

112

"""Initialize UDP server.

113

114

Parameters:

115

- server_address: (IP, port) tuple to bind to

116

- dispatcher: Dispatcher for routing messages

117

- bind_and_activate: Whether to bind and activate immediately

118

"""

119

120

@property

121

def dispatcher(self) -> Dispatcher:

122

"""Access to message dispatcher."""

123

124

def verify_request(self, request, client_address) -> bool:

125

"""Validate incoming requests.

126

127

Parameters:

128

- request: Raw request data

129

- client_address: Client address tuple

130

131

Returns:

132

True if request should be processed

133

"""

134

135

class BlockingOSCUDPServer(OSCUDPServer):

136

"""Sequential UDP server handling one message at a time."""

137

138

def serve_forever(self):

139

"""Start server and handle requests sequentially."""

140

141

class ThreadingOSCUDPServer(OSCUDPServer):

142

"""UDP server spawning thread per message for concurrent handling."""

143

144

def serve_forever(self):

145

"""Start server with thread-per-message handling."""

146

147

class ForkingOSCUDPServer(OSCUDPServer):

148

"""UDP server spawning process per message (Unix only)."""

149

150

def serve_forever(self):

151

"""Start server with process-per-message handling."""

152

153

class AsyncIOOSCUDPServer:

154

"""Asynchronous UDP server using asyncio event loop."""

155

156

def __init__(self, server_address: Tuple[str, int], dispatcher: Dispatcher, loop: asyncio.BaseEventLoop):

157

"""Initialize async UDP server.

158

159

Parameters:

160

- server_address: (IP, port) tuple to bind to

161

- dispatcher: Dispatcher for routing messages

162

- loop: asyncio event loop to use for the server

163

"""

164

165

@property

166

def dispatcher(self) -> Dispatcher:

167

"""Access to message dispatcher."""

168

169

def serve(self):

170

"""Start server and run forever asynchronously."""

171

172

def create_serve_endpoint(self):

173

"""Create datagram endpoint coroutine for integration."""

174

```

175

176

### TCP Servers

177

178

TCP server implementations supporting OSC 1.0 and 1.1 protocols with connection-based handling.

179

180

```python { .api }

181

class OSCTCPServer:

182

"""Base TCP server class supporting OSC 1.0 and 1.1 protocols."""

183

184

def __init__(self, server_address: Tuple[str, int], dispatcher: Dispatcher, mode: str = "1.1"):

185

"""Initialize TCP server.

186

187

Parameters:

188

- server_address: (IP, port) tuple to bind to

189

- dispatcher: Dispatcher for routing messages

190

- mode: OSC protocol mode ("1.0" or "1.1")

191

"""

192

193

@property

194

def dispatcher(self) -> Dispatcher:

195

"""Access to message dispatcher."""

196

197

class BlockingOSCTCPServer(OSCTCPServer):

198

"""Sequential TCP server handling one connection at a time."""

199

200

def serve_forever(self):

201

"""Start server and handle connections sequentially."""

202

203

class ThreadingOSCTCPServer(OSCTCPServer):

204

"""TCP server spawning thread per connection for concurrent handling."""

205

206

def serve_forever(self):

207

"""Start server with thread-per-connection handling."""

208

209

class ForkingOSCTCPServer(OSCTCPServer):

210

"""TCP server spawning process per connection (Unix only)."""

211

212

def serve_forever(self):

213

"""Start server with process-per-connection handling."""

214

215

class AsyncOSCTCPServer:

216

"""Asynchronous TCP server with asyncio integration."""

217

218

def __init__(self, server_address: str, port: int, dispatcher: Dispatcher, mode: str = "1.1"):

219

"""Initialize async TCP server.

220

221

Parameters:

222

- server_address: IP address to bind to

223

- port: TCP port to bind to

224

- dispatcher: Dispatcher for routing messages

225

- mode: OSC protocol mode ("1.0" or "1.1")

226

"""

227

228

@property

229

def dispatcher(self) -> Dispatcher:

230

"""Access to message dispatcher."""

231

232

async def start(self):

233

"""Start server coroutine."""

234

235

async def stop(self):

236

"""Stop server and close connections."""

237

238

async def handle(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):

239

"""Handle individual client connections.

240

241

Parameters:

242

- reader: asyncio stream reader for receiving data

243

- writer: asyncio stream writer for sending responses

244

"""

245

```

246

247

## Usage Examples

248

249

### Basic UDP Server

250

251

```python

252

from pythonosc.dispatcher import Dispatcher

253

from pythonosc import osc_server

254

import threading

255

256

# Define message handlers

257

def volume_handler(address, *args):

258

print(f"Volume: {args[0]}")

259

260

def filter_handler(address, *args):

261

print(f"Filter: {args[0]}")

262

263

def default_handler(address, *args):

264

print(f"Unhandled: {address} {args}")

265

266

# Set up dispatcher

267

dispatcher = Dispatcher()

268

dispatcher.map("/volume", volume_handler)

269

dispatcher.map("/filter", filter_handler)

270

dispatcher.set_default_handler(default_handler)

271

272

# Create and start server

273

server = osc_server.ThreadingOSCUDPServer(("127.0.0.1", 5005), dispatcher)

274

print(f"OSC Server running on {server.server_address}")

275

276

# Run server in background thread

277

server_thread = threading.Thread(target=server.serve_forever)

278

server_thread.daemon = True

279

server_thread.start()

280

281

# Server runs until program exits

282

```

283

284

### Advanced Handler Patterns

285

286

```python

287

from pythonosc.dispatcher import Dispatcher

288

from pythonosc import osc_server

289

290

# Handler with fixed arguments

291

def parametric_handler(address, channel, *osc_args):

292

print(f"Channel {channel}: {address} -> {osc_args}")

293

294

# Handler needing client address for responses

295

def response_handler(client_address, address, *args):

296

client_ip, client_port = client_address

297

print(f"Request from {client_ip}:{client_port}: {address} {args}")

298

return ("/ack", f"processed_{args[0]}") # Return response

299

300

# Wildcard pattern handler

301

def synth_handler(address, *args):

302

# Handle all /synth/* addresses

303

param = address.split('/')[-1] # Get last part of address

304

print(f"Synth parameter {param}: {args}")

305

306

# Set up complex dispatcher

307

dispatcher = Dispatcher()

308

309

# Map with fixed arguments

310

dispatcher.map("/mixer/channel/1", parametric_handler, "Channel 1")

311

dispatcher.map("/mixer/channel/2", parametric_handler, "Channel 2")

312

313

# Map handler that needs client address

314

dispatcher.map("/request/*", response_handler, needs_reply_address=True)

315

316

# Map wildcard patterns

317

dispatcher.map("/synth/*", synth_handler)

318

319

# Create server

320

server = osc_server.ThreadingOSCUDPServer(("0.0.0.0", 8000), dispatcher)

321

server.serve_forever()

322

```

323

324

### TCP Server with Protocol Modes

325

326

```python

327

from pythonosc.dispatcher import Dispatcher

328

from pythonosc import osc_tcp_server

329

import threading

330

331

def message_handler(address, *args):

332

print(f"TCP: {address} -> {args}")

333

334

# Set up dispatcher

335

dispatcher = Dispatcher()

336

dispatcher.map("/*", message_handler) # Handle all messages

337

338

# OSC 1.1 server (SLIP-encoded, default)

339

server_11 = osc_tcp_server.ThreadingOSCTCPServer(("127.0.0.1", 9000), dispatcher, mode="1.1")

340

thread_11 = threading.Thread(target=server_11.serve_forever)

341

thread_11.daemon = True

342

thread_11.start()

343

344

# OSC 1.0 server (size-prefixed)

345

server_10 = osc_tcp_server.ThreadingOSCTCPServer(("127.0.0.1", 9001), dispatcher, mode="1.0")

346

thread_10 = threading.Thread(target=server_10.serve_forever)

347

thread_10.daemon = True

348

thread_10.start()

349

350

print("TCP servers running on ports 9000 (OSC 1.1) and 9001 (OSC 1.0)")

351

```

352

353

### Asyncio UDP Server

354

355

```python

356

import asyncio

357

from pythonosc.dispatcher import Dispatcher

358

from pythonosc import osc_server

359

360

async def async_handler(address, *args):

361

print(f"Async handler: {address} -> {args}")

362

# Can perform async operations here

363

await asyncio.sleep(0.1)

364

365

# Set up dispatcher with async handler

366

dispatcher = Dispatcher()

367

dispatcher.map("/async/*", async_handler)

368

369

async def run_async_server():

370

# Get current event loop

371

loop = asyncio.get_running_loop()

372

373

# Create async UDP server

374

server = osc_server.AsyncIOOSCUDPServer(("127.0.0.1", 6000), dispatcher, loop)

375

376

# Start server

377

await server.serve()

378

379

# Run async server

380

asyncio.run(run_async_server())

381

```

382

383

### Asyncio TCP Server

384

385

```python

386

import asyncio

387

from pythonosc.dispatcher import Dispatcher

388

from pythonosc import osc_tcp_server

389

390

async def async_tcp_handler(address, *args):

391

print(f"Async TCP: {address} -> {args}")

392

return ("/response", f"processed_{len(args)}_args")

393

394

async def run_async_tcp_server():

395

# Set up dispatcher

396

dispatcher = Dispatcher()

397

dispatcher.map("/*", async_tcp_handler)

398

399

# Create and start async TCP server

400

server = osc_tcp_server.AsyncOSCTCPServer("127.0.0.1", 9002, dispatcher)

401

await server.start()

402

403

print("Async TCP server started on port 9002")

404

405

# Keep server running

406

try:

407

while True:

408

await asyncio.sleep(1)

409

except KeyboardInterrupt:

410

await server.stop()

411

print("Server stopped")

412

413

asyncio.run(run_async_tcp_server())

414

```

415

416

### Multi-Protocol Server Setup

417

418

```python

419

from pythonosc.dispatcher import Dispatcher

420

from pythonosc import osc_server, osc_tcp_server

421

import threading

422

import time

423

424

# Shared message handler

425

def unified_handler(address, *args):

426

print(f"Received: {address} -> {args}")

427

428

# Create shared dispatcher

429

dispatcher = Dispatcher()

430

dispatcher.map("/*", unified_handler)

431

432

# Start UDP server

433

udp_server = osc_server.ThreadingOSCUDPServer(("0.0.0.0", 5005), dispatcher)

434

udp_thread = threading.Thread(target=udp_server.serve_forever)

435

udp_thread.daemon = True

436

udp_thread.start()

437

438

# Start TCP servers

439

tcp_server_10 = osc_tcp_server.ThreadingOSCTCPServer(("0.0.0.0", 8000), dispatcher, mode="1.0")

440

tcp_thread_10 = threading.Thread(target=tcp_server_10.serve_forever)

441

tcp_thread_10.daemon = True

442

tcp_thread_10.start()

443

444

tcp_server_11 = osc_tcp_server.ThreadingOSCTCPServer(("0.0.0.0", 8001), dispatcher, mode="1.1")

445

tcp_thread_11 = threading.Thread(target=tcp_server_11.serve_forever)

446

tcp_thread_11.daemon = True

447

tcp_thread_11.start()

448

449

print("Multi-protocol server running:")

450

print(" UDP on port 5005")

451

print(" TCP OSC 1.0 on port 8000")

452

print(" TCP OSC 1.1 on port 8001")

453

454

# Keep main thread alive

455

try:

456

while True:

457

time.sleep(1)

458

except KeyboardInterrupt:

459

print("Shutting down servers...")

460

```

461

462

### Server with Response Messages

463

464

```python

465

from pythonosc.dispatcher import Dispatcher

466

from pythonosc import osc_server

467

468

def request_handler(client_address, address, *args):

469

"""Handler that sends responses back to client."""

470

client_ip, client_port = client_address

471

472

if address == "/get/status":

473

return ("/status", "running", 1.0)

474

elif address == "/get/info":

475

return ("/info", ["server", "v1.0", client_ip])

476

elif address == "/echo":

477

return ("/echo_response", *args)

478

else:

479

return ("/error", f"unknown_request: {address}")

480

481

# Set up dispatcher with response handler

482

dispatcher = Dispatcher()

483

dispatcher.map("/get/*", request_handler, needs_reply_address=True)

484

dispatcher.map("/echo", request_handler, needs_reply_address=True)

485

486

# Create server that can send responses

487

server = osc_server.ThreadingOSCUDPServer(("127.0.0.1", 7000), dispatcher)

488

print("Response server running on port 7000")

489

server.serve_forever()

490

```

491

492

### Server Performance Monitoring

493

494

```python

495

from pythonosc.dispatcher import Dispatcher

496

from pythonosc import osc_server

497

import time

498

import threading

499

500

class MonitoringHandler:

501

def __init__(self):

502

self.message_count = 0

503

self.start_time = time.time()

504

505

def handle_message(self, address, *args):

506

self.message_count += 1

507

if self.message_count % 1000 == 0:

508

elapsed = time.time() - self.start_time

509

rate = self.message_count / elapsed

510

print(f"Processed {self.message_count} messages ({rate:.1f} msg/sec)")

511

512

def get_stats(self, address, *args):

513

elapsed = time.time() - self.start_time

514

rate = self.message_count / elapsed if elapsed > 0 else 0

515

return ("/stats", self.message_count, elapsed, rate)

516

517

# Set up monitoring

518

monitor = MonitoringHandler()

519

dispatcher = Dispatcher()

520

dispatcher.map("/*", monitor.handle_message)

521

dispatcher.map("/get/stats", monitor.get_stats, needs_reply_address=True)

522

523

# High-performance server

524

server = osc_server.ThreadingOSCUDPServer(("0.0.0.0", 5555), dispatcher)

525

print("Monitoring server running on port 5555")

526

print("Send messages to any address, or /get/stats for statistics")

527

server.serve_forever()

528

```

529

530

## Types and Imports

531

532

```python { .api }

533

from typing import Callable, Union, List, Tuple, Generator, Any

534

import threading

535

import asyncio

536

537

from pythonosc.osc_message import OscMessage

538

from pythonosc.dispatcher import Dispatcher, Handler

539

```