added rtmt
This commit is contained in:
parent
7829e53ecf
commit
8e0d7d5d57
29
package-lock.json
generated
29
package-lock.json
generated
@ -62,6 +62,11 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/cors": {
|
||||||
|
"version": "2.8.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz",
|
||||||
|
"integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ=="
|
||||||
|
},
|
||||||
"@types/express": {
|
"@types/express": {
|
||||||
"version": "4.17.12",
|
"version": "4.17.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz",
|
||||||
@ -118,6 +123,11 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/uuid": {
|
||||||
|
"version": "8.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz",
|
||||||
|
"integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ=="
|
||||||
|
},
|
||||||
"@types/ws": {
|
"@types/ws": {
|
||||||
"version": "7.4.5",
|
"version": "7.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.5.tgz",
|
||||||
@ -434,6 +444,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||||
},
|
},
|
||||||
|
"cors": {
|
||||||
|
"version": "2.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
|
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||||
|
"requires": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"create-require": {
|
"create-require": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
@ -1006,6 +1025,11 @@
|
|||||||
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
|
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||||
|
},
|
||||||
"on-finished": {
|
"on-finished": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
@ -1482,6 +1506,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||||
},
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||||
|
},
|
||||||
"vary": {
|
"vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
|||||||
@ -12,8 +12,12 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/cors": "^2.8.10",
|
||||||
|
"@types/uuid": "^8.3.0",
|
||||||
"@types/ws": "^7.4.5",
|
"@types/ws": "^7.4.5",
|
||||||
|
"cors": "^2.8.5",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
|
"uuid": "^8.3.2",
|
||||||
"ws": "^7.5.0"
|
"ws": "^7.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
27
src/index.ts
27
src/index.ts
@ -1,27 +1,34 @@
|
|||||||
import express, { Request, Response } from "express";
|
import express, { Request, Response } from "express";
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import WebSocket from "ws"
|
import WebSocket from "ws"
|
||||||
|
import { encodeText } from "./rtmt/byte_util";
|
||||||
|
import { encode } from "./rtmt/encode_decode_message";
|
||||||
|
import { RTMTWS } from "./rtmt/rtmt_websocket";
|
||||||
|
import cors from "cors"
|
||||||
|
import { generateKey } from "./key_factory";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
app.use(cors())
|
||||||
|
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
|
|
||||||
const ws = new WebSocket.Server({server})
|
|
||||||
|
|
||||||
ws.on("connection", (ws : WebSocket)=>{
|
|
||||||
ws.on("message", (message : string)=>{
|
|
||||||
console.log('received: %s', message);
|
|
||||||
ws.send(`Hello, you sent -> ${message}`);
|
|
||||||
})
|
|
||||||
ws.send('Hi there, I am a WebSocket server');
|
|
||||||
})
|
|
||||||
|
|
||||||
app.get("/", (req: Request, res: Response) => {
|
app.get("/", (req: Request, res: Response) => {
|
||||||
res.send("Hello World");
|
res.send("Hello World");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get("/register/", (req: Request, res: Response) => {
|
||||||
|
res.send(generateKey());
|
||||||
|
});
|
||||||
|
|
||||||
const port = process.env.PORT || 5000
|
const port = process.env.PORT || 5000
|
||||||
|
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
console.log(`Server started on port ${port} :)`);
|
console.log(`Server started on port ${port} :)`);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const rtmt = new RTMTWS()
|
||||||
|
|
||||||
|
rtmt.initWebSocket(server, ()=>{
|
||||||
|
|
||||||
|
})
|
||||||
6
src/key_factory.ts
Normal file
6
src/key_factory.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
|
||||||
|
export const generateKey = ()=>{
|
||||||
|
return uuidv4();
|
||||||
|
}
|
||||||
8
src/mancala.ts
Normal file
8
src/mancala.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
type User = {
|
||||||
|
id : Number,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Game = {
|
||||||
|
user1 : User,
|
||||||
|
user2 : User,
|
||||||
|
}
|
||||||
14
src/rtmt/byte_util.ts
Normal file
14
src/rtmt/byte_util.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Bytes } from "./rtmt"
|
||||||
|
var util= require('util');
|
||||||
|
|
||||||
|
const textEncoder = new util.TextEncoder()
|
||||||
|
const textDecoder = new util.TextDecoder("utf-8")
|
||||||
|
|
||||||
|
export function encodeText(text : string) {
|
||||||
|
const bytes = textEncoder.encode(text)
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeText(bytes : Bytes) {
|
||||||
|
return textDecoder.decode(bytes)
|
||||||
|
}
|
||||||
58
src/rtmt/encode_decode_message.ts
Normal file
58
src/rtmt/encode_decode_message.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { decodeText, encodeText } from "./byte_util";
|
||||||
|
import { Bytes } from "./rtmt";
|
||||||
|
|
||||||
|
const headerLenght = 4
|
||||||
|
|
||||||
|
export type Message = {
|
||||||
|
channel : string,
|
||||||
|
data : Bytes,
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// channel is string, message is byte array
|
||||||
|
//
|
||||||
|
export function encode(message : Message) {
|
||||||
|
const {channel, data} = message
|
||||||
|
const channelLenght = channel.length
|
||||||
|
const messageLenght = data.length
|
||||||
|
const totalLenght = headerLenght + channelLenght + messageLenght
|
||||||
|
|
||||||
|
const buffer = new ArrayBuffer(totalLenght);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
|
||||||
|
view.setUint32(0, channelLenght);
|
||||||
|
|
||||||
|
const channelBytes = encodeText(channel)
|
||||||
|
|
||||||
|
let count = headerLenght
|
||||||
|
channelBytes.forEach((byte : any)=>{
|
||||||
|
view.setUint8(count, byte)
|
||||||
|
count++
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const byte of data) {
|
||||||
|
view.setUint8(count, byte)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// return { channel : string, message : byte array}
|
||||||
|
//
|
||||||
|
export function decode(bytes : Bytes) : Message {
|
||||||
|
const view = new DataView(bytes.buffer);
|
||||||
|
|
||||||
|
const channelLenght = view.getUint32(0)
|
||||||
|
|
||||||
|
|
||||||
|
const channel = decodeText(
|
||||||
|
bytes.slice(headerLenght, headerLenght + channelLenght))
|
||||||
|
|
||||||
|
const message = bytes.slice(headerLenght + channelLenght)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"channel": channel,
|
||||||
|
"data": message,
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/rtmt/rtmt.ts
Normal file
7
src/rtmt/rtmt.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export type Bytes = UInt8Array
|
||||||
|
export type OnMessage = (clientID : string, message : Bytes) => any
|
||||||
|
|
||||||
|
export interface RTMT{
|
||||||
|
sendMessage : (clientID : string, channel : string, message : Bytes) => any
|
||||||
|
listenMessage : (channel : string, callback : OnMessage) => any
|
||||||
|
}
|
||||||
81
src/rtmt/rtmt_websocket.ts
Normal file
81
src/rtmt/rtmt_websocket.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { decode, encode } from "./encode_decode_message"
|
||||||
|
import { Bytes, OnMessage, RTMT } from "./rtmt"
|
||||||
|
|
||||||
|
import WebSocket from "ws"
|
||||||
|
import * as http from 'http';
|
||||||
|
|
||||||
|
export class RTMTWS implements RTMT {
|
||||||
|
|
||||||
|
private messageChannels: Map<String, OnMessage>
|
||||||
|
|
||||||
|
private wsServer: WebSocket.Server | null
|
||||||
|
|
||||||
|
public clients = new Map<string, WebSocket>()
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.messageChannels = new Map<String, OnMessage>()
|
||||||
|
this.wsServer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
initWebSocket(server: http.Server, onopen: () => any) {
|
||||||
|
const wsServer = new WebSocket.Server({ server })
|
||||||
|
this.clients = new Map<string, WebSocket>()
|
||||||
|
|
||||||
|
wsServer.on("connection", (ws: WebSocket, req: Request) => {
|
||||||
|
console.log(req.url);
|
||||||
|
const regexResult = req.url.split(RegExp("\/\?userKey="));
|
||||||
|
const clientID = regexResult[1]
|
||||||
|
console.log(regexResult);
|
||||||
|
this.clients.set(clientID, ws)
|
||||||
|
ws.on("message", (messageBytes: Bytes) => {
|
||||||
|
console.log('received: %s', messageBytes);
|
||||||
|
this.onWebSocketMessage("0", messageBytes)
|
||||||
|
})
|
||||||
|
|
||||||
|
ws.on("close", (code: number, reason: string) => {
|
||||||
|
console.log("WS Closed! code : " + code + " reason : " + reason);
|
||||||
|
this.clients.delete(clientID)
|
||||||
|
})
|
||||||
|
|
||||||
|
ws.on("error", (err: Error) => {
|
||||||
|
console.log("WS Closed with error! error : " + err.message);
|
||||||
|
this.clients.delete(clientID)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(clientID: string, channel: string, message: Bytes) {
|
||||||
|
if (this.wsServer) {
|
||||||
|
const client = this.clients.get(clientID)
|
||||||
|
if (client) {
|
||||||
|
const data = encode({ channel: channel, data: message })
|
||||||
|
console.log("(RTMT) Sending message to channel " + channel);
|
||||||
|
client.send(data)
|
||||||
|
} else {
|
||||||
|
console.log("Client not connected!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('ws is undefined')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listenMessage(channel: string, callback: OnMessage) {
|
||||||
|
this.messageChannels.set(channel, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
onWebSocketMessage(clientID: string, messageBytes: Bytes) {
|
||||||
|
const message = decode(messageBytes)
|
||||||
|
this.onMessage(clientID, message.channel, message.data)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessage(clientID: string, channel: string, message: Int8Array) {
|
||||||
|
const callback = this.messageChannels.get(channel)
|
||||||
|
if (callback) {
|
||||||
|
callback(clientID, message)
|
||||||
|
} else {
|
||||||
|
console.log("Channel callback not found! " + channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user