Skip to content

Commit dd58db4

Browse files
committed
wip
1 parent 32783c7 commit dd58db4

File tree

8 files changed

+498
-4
lines changed

8 files changed

+498
-4
lines changed

package-lock.json

Lines changed: 46 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/sctp/README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,89 @@ await Promise.all([
3939
client.send(0, WEBRTC_PPID.STRING, Buffer.from("ping"));
4040
```
4141

42+
## Session Migration
43+
44+
werift-sctp supports session state serialization and restoration, allowing you to migrate SCTP sessions between different processes or network endpoints while preserving connection state.
45+
46+
### Usage
47+
48+
```typescript
49+
import { SCTP } from "werift-sctp";
50+
51+
// Export session state
52+
const stateBuffer = sctpSession.exportState();
53+
54+
// Restore session with new transport
55+
const newSession = SCTP.restoreState(stateBuffer, newTransport);
56+
```
57+
58+
### Example
59+
60+
```typescript
61+
import { createSocket } from "dgram";
62+
import { SCTP, WEBRTC_PPID, createUdpTransport } from "werift-sctp";
63+
import { randomPort } from "werift-common";
64+
65+
async function sessionMigrationExample() {
66+
const clientPort = await randomPort();
67+
const serverPort = await randomPort();
68+
69+
// Create initial session
70+
const sctpA = SCTP.client(
71+
createUdpTransport(createSocket("udp4").bind(clientPort), {
72+
port: serverPort,
73+
address: "127.0.0.1",
74+
}),
75+
);
76+
77+
const sctpB = SCTP.server(
78+
createUdpTransport(createSocket("udp4").bind(serverPort), {
79+
port: clientPort,
80+
address: "127.0.0.1",
81+
}),
82+
);
83+
84+
// Establish connection
85+
await Promise.all([sctpA.start(), sctpB.start()]);
86+
87+
// Send data
88+
await sctpA.send(0, WEBRTC_PPID.STRING, Buffer.from("Hello"));
89+
90+
// Export session state
91+
const stateBuffer = sctpA.exportState();
92+
93+
// Create new transport for migration
94+
const newClientPort = await randomPort();
95+
const newTransport = createUdpTransport(createSocket("udp4").bind(newClientPort), {
96+
port: serverPort,
97+
address: "127.0.0.1",
98+
});
99+
100+
// Restore session
101+
const sctpA2 = SCTP.restoreState(stateBuffer, newTransport);
102+
103+
// Update server's remote port
104+
sctpB.setRemotePort(newClientPort);
105+
106+
// Stop old session
107+
await sctpA.stop();
108+
109+
// Continue communication with migrated session
110+
await sctpA2.send(0, WEBRTC_PPID.STRING, Buffer.from("Migrated Hello"));
111+
112+
// Cleanup
113+
await sctpA2.stop();
114+
await sctpB.stop();
115+
}
116+
```
117+
118+
### Features
119+
120+
- **Complete state preservation**: All session state including streams, sequence numbers, and connection parameters are preserved
121+
- **MessagePack serialization**: Efficient binary serialization using MessagePack
122+
- **Multiple stream support**: Stream state is maintained across migration
123+
- **Transport flexibility**: Works with any transport implementation
124+
42125
# reference
43126

44127
-
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { randomPort } from "../../common/src";
2+
import { SCTP, WEBRTC_PPID } from "../src";
3+
import { createUdpTransport } from "../src/transport";
4+
import { createSocket } from "node:dgram";
5+
6+
async function main() {
7+
console.log("SCTP Session Migration Example with UDP Transport");
8+
9+
// Get random ports for UDP communication
10+
const clientPort = await randomPort();
11+
const serverPort = await randomPort();
12+
13+
console.log(`Client port: ${clientPort}, Server port: ${serverPort}`);
14+
15+
// Create SCTP sessions with UDP transport
16+
const sctpA = SCTP.client(
17+
createUdpTransport(createSocket("udp4").bind(clientPort), {
18+
port: serverPort,
19+
address: "127.0.0.1",
20+
}),
21+
);
22+
const sctpB = SCTP.server(
23+
createUdpTransport(createSocket("udp4").bind(serverPort), {
24+
port: clientPort,
25+
address: "127.0.0.1",
26+
}),
27+
);
28+
29+
// Set up event listeners
30+
sctpB.onReceive.subscribe((streamId, ppId, data) => {
31+
console.log(`Received from stream ${streamId}: ${data.toString()}`);
32+
});
33+
34+
// Start sessions and wait for connection
35+
console.log("Starting SCTP sessions...");
36+
await Promise.all([sctpA.start(), sctpB.start()]);
37+
38+
// Wait for connection to be established
39+
await new Promise<void>((resolve, reject) => {
40+
const timeout = setTimeout(() => reject(new Error("Connection timeout")), 5000);
41+
const checkConnection = () => {
42+
if (sctpA.state === "connected" && sctpB.state === "connected") {
43+
clearTimeout(timeout);
44+
resolve();
45+
} else {
46+
setTimeout(checkConnection, 10);
47+
}
48+
};
49+
checkConnection();
50+
});
51+
52+
console.log("Connection established");
53+
54+
// Send some data from session A
55+
await sctpA.send(0, WEBRTC_PPID.STRING, Buffer.from("Hello from session A"));
56+
console.log("Sent data from session A");
57+
58+
// Send data on multiple streams
59+
await sctpA.send(1, WEBRTC_PPID.STRING, Buffer.from("Stream 1 data"));
60+
console.log("Sent data on stream 1");
61+
62+
// Wait a bit for data to be processed
63+
await new Promise(resolve => setTimeout(resolve, 200));
64+
65+
// Export session A state
66+
console.log("Exporting session A state...");
67+
const stateBuffer = sctpA.exportState();
68+
console.log(`Exported state size: ${stateBuffer.length} bytes`);
69+
70+
// Create new transport for restored session with a new port
71+
const newClientPort = await randomPort();
72+
console.log(`New client port for migration: ${newClientPort}`);
73+
74+
const transportA2 = createUdpTransport(createSocket("udp4").bind(newClientPort), {
75+
port: serverPort,
76+
address: "127.0.0.1",
77+
});
78+
79+
// Restore session from exported state
80+
console.log("Restoring session from exported state...");
81+
const sctpA2 = SCTP.restoreState(stateBuffer, transportA2);
82+
83+
// Update server's remote port to communicate with the new client
84+
sctpB.setRemotePort(newClientPort);
85+
86+
console.log("Session migrated successfully");
87+
console.log(`Original session state: ${sctpA.state}`);
88+
console.log(`Restored session state: ${sctpA2.state}`);
89+
console.log(`Restored session is server: ${sctpA2.isServer}`);
90+
console.log(`Restored session port: ${sctpA2.port}`);
91+
console.log(`Restored session max channels: ${sctpA2.maxChannels}`);
92+
93+
// Stop original session
94+
await sctpA.stop();
95+
console.log("Original session stopped");
96+
97+
// Send data from restored session
98+
await sctpA2.send(0, WEBRTC_PPID.STRING, Buffer.from("Hello from migrated session A"));
99+
console.log("Sent data from migrated session on stream 0");
100+
101+
await sctpA2.send(1, WEBRTC_PPID.STRING, Buffer.from("Migrated stream 1 data"));
102+
console.log("Sent data from migrated session on stream 1");
103+
104+
// Wait for messages to be received
105+
await new Promise(resolve => setTimeout(resolve, 200));
106+
107+
// Clean up
108+
await sctpA2.stop();
109+
await sctpB.stop();
110+
111+
console.log("Session migration example completed successfully");
112+
}
113+
114+
if (require.main === module) {
115+
main().catch(console.error);
116+
}

packages/sctp/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@
3131
"dependencies": {
3232
"@shinyoshiaki/jspack": "^0.0.6",
3333
"lodash": "^4.17.21",
34+
"msgpack-lite": "^0.1.26",
3435
"turbo-crc32": "^1.0.1"
3536
},
3637
"devDependencies": {
37-
"@types/lodash": "^4.17.15"
38+
"@types/lodash": "^4.17.15",
39+
"@types/msgpack-lite": "^0.1.11"
3840
},
3941
"engines": {
4042
"node": ">=10"

packages/sctp/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export { SCTP_STATE, WEBRTC_PPID } from "./const";
2-
export { SCTP } from "./sctp";
2+
export { SCTP, type SCTPDto } from "./sctp";
33
export type { Transport } from "./transport";
44
export { SCTPTimerManager, SCTPTimerType } from "./timer";

packages/sctp/src/sctp.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createHmac, randomBytes } from "crypto";
22
import { jspack } from "@shinyoshiaki/jspack";
3+
import * as msgpack from "msgpack-lite";
34

45
import {
56
AbortChunk,
@@ -647,7 +648,7 @@ export class SCTP {
647648
};
648649
}
649650

650-
fromDto(dto: SCTPDto, transport: Transport) {
651+
static fromDto(dto: SCTPDto, transport: Transport) {
651652
const sctp = new SCTP(transport);
652653
sctp.associationState = dto.associationState;
653654
sctp.started = dto.started;
@@ -677,6 +678,15 @@ export class SCTP {
677678
sctp.setup();
678679
return sctp;
679680
}
681+
682+
exportState(): Buffer {
683+
return msgpack.encode(this.toDto());
684+
}
685+
686+
static restoreState(data: Buffer, transport: Transport): SCTP {
687+
const dto: SCTPDto = msgpack.decode(data);
688+
return SCTP.fromDto(dto, transport);
689+
}
680690
}
681691

682692
export interface SCTPDto {

0 commit comments

Comments
 (0)