Code Monkey home page Code Monkey logo

udp-ws's Introduction

udp-ws

Motivation

“I feel what is needed is an UDP version of WebSockets. That’s all I wish we had.” - Matheus Valadares, creator of agar.io

WebSocket is built on TCP. However, UDP is much preferred over TCP for networking in realtime multiplayer games. Refer to the awesome visualizations in Gaffer On Games: Deterministic Lockstep to see why.

udp-ws is a UDP version of WebSocket built on WebRTC, which allows peer-to-peer UDP communication in the browser. However, udp-ws is designed for client-server communication (e.g. for server-authoritative browser games), not for peer-to-peer communication. udp-ws accomplishes this by treating your server as a peer.

udp-ws is inspired by and includes code snippets from geckos.io by yandeu and node-webrtc-examples.


Examples

1. examples/barebones/ - Barebones example of


Client (resembles WebSocket):

const ws = new UDPWebSocket('ws://localhost:3000');

ws.onmessage = ev => {
    console.log(ev.data);
};

setInterval(() => {
    if (ws.readyState === 'open') {
        ws.send('client says hi');
    }
}, 1000);

Server (resembles ws):

const wss = new UDPWebSocketServer(3000);

wss.on('connection', ws => {
    ws.on('message', data => {
        console.log(data);
        if (ws.readyState === 'open') {
            ws.send('server says hi');
        }
    });
});

In examples/barebones/server/, run npm i followed by npm run launch, and see the client-server example running in localhost. Observe the output in your console log (e.g. using Ctrl + Shift + I in Windows Chrome).


2. examples/handler/ - Handling WebSocket life cycle, messaging, and event callbacks using


Structure of packet: object conforming to JSON.stringify and JSON.parse:

{
    event: 'string representing event',
    data: { arbitrary object representing data }
}

Client:

(async () => {
    // WebSocketType.TCP for WebSocket, WebSocketType.UDP for UDPWebSocket
    const webSocketHandler = new WebSocketHandler('ws://localhost:3000', WebSocketType.UDP);

    webSocketHandler.bind('ServerResponseEvent', data => {
        console.log(data);
    });

    try {
        await webSocketHandler.connect();

        // Code that must be executed only after the WebSocket is open
        setInterval(() => {
            webSocketHandler.send({
                event: 'ClientMessageEvent',
                data: {
                    message: 'client says hi'
                }
            });
        }, 1000);
    } catch (err) {
        throw err;
    }
})();

Server:

// WebSocketType.TCP for WebSocket, WebSocketType.UDP for UDPWebSocket
const webSocketServerHandler = new WebSocketServerHandler(3000, WebSocketType.UDP);

webSocketServerHandler.bind('ClientMessageEvent', (iws, data) => {
    console.log(data);
    webSocketServerHandler.send(iws, {
        event: 'ServerResponseEvent',
        data: {
            reply: 'server says hi'
        }
    });
});

In examples/handler/server/, run npm i followed by npm run launch.


3. examples/binaryHandler/ - Handling WebSocket life cycle, binary messaging, and event callbacks using


Structure of packet: ArrayBuffer with leading Uint8 byte representing the event (thus 2^8 = 256 maximum handled events unless Uint8 is replaced with something greater, e.g. Uint16) followed by an arbitrary number of bytes representing the data

ArrayBuffer(NUM_BYTES_UINT8 + NUM_BYTES_UINT32 * X + NUM_BYTES_FLOAT64 * Y + NUM_BYTES_CHAR * Z + ...)

Client:

import { NUM_BYTES_UINT8, NUM_BYTES_FLOAT64, NUM_BYTES_CHAR, writeStringToBuffer, bufferToString } from './binaryTools';

// Uint8 representing event (NumberEvent = 0, StringEvent = 1),
// which is the first byte of each packet sent and received
enum WebSocketEvent {
    NumberEvent = 0,
    StringEvent
}

(async () => {
    // WebSocketType.TCP for WebSocket, WebSocketType.UDP for UDPWebSocket
    const webSocketHandler = new BinaryWebSocketHandler('ws://localhost:3000', WebSocketType.UDP);

    webSocketHandler.bind(WebSocketEvent.NumberEvent, buffer => {
        const view = new DataView(buffer);
        // read Float64 after first byte representing event
        console.log(view.getFloat64(NUM_BYTES_UINT8));
    });

    webSocketHandler.bind(WebSocketEvent.StringEvent, buffer => {
        // read string after first byte representing event
        console.log(bufferToString(buffer.slice(NUM_BYTES_UINT8)));
    });

    try {
        await webSocketHandler.connect();
        
        // Code that must be executed only after the WebSocket is open
        setInterval(() => {
            const buffer = new ArrayBuffer(NUM_BYTES_UINT8 + NUM_BYTES_FLOAT64);
            const view = new DataView(buffer);
            // set first byte representing event
            view.setUint8(0, WebSocketEvent.NumberEvent);
            // set Float64 after first byte
            view.setFloat64(NUM_BYTES_UINT8, 12345.6789);
            webSocketHandler.send(buffer);
        }, 1000);

        setInterval(() => {
            // 14 characters needed for 'client says hi'
            const buffer = new ArrayBuffer(NUM_BYTES_UINT8 + NUM_BYTES_CHAR * 14);
            const view = new DataView(buffer);
            // set first byte representing event
            view.setUint8(0, WebSocketEvent.StringEvent);
            // set string after first byte
            writeStringToBuffer('client says hi', buffer, NUM_BYTES_UINT8);
            webSocketHandler.send(buffer);
        }, 1000);
    } catch (err) {
        throw err;
    }
})();

Server:

import { NUM_BYTES_UINT8, NUM_BYTES_FLOAT64, NUM_BYTES_CHAR, writeStringToBuffer, bufferToString } from './binaryTools';

enum WebSocketEvent {
    NumberEvent = 0,
    StringEvent
}

// WebSocketType.TCP for WebSocket, WebSocketType.UDP for UDPWebSocket
const webSocketServerHandler = new BinaryWebSocketServerHandler(3000, WebSocketType.UDP);

webSocketServerHandler.bind(WebSocketEvent.NumberEvent, (iws, buffer) => {
    const inView = new DataView(buffer);
    console.log(inView.getFloat64(NUM_BYTES_UINT8));

    const outBuffer = new ArrayBuffer(NUM_BYTES_UINT8 + NUM_BYTES_FLOAT64);
    const outView = new DataView(outBuffer);
    outView.setUint8(0, WebSocketEvent.NumberEvent);
    outView.setFloat64(NUM_BYTES_UINT8, 9876.54321);
    webSocketServerHandler.send(iws, outBuffer);
});

webSocketServerHandler.bind(WebSocketEvent.StringEvent, (iws, buffer) => {
    console.log(bufferToString(buffer.slice(NUM_BYTES_UINT8)));

    const outBuffer = new ArrayBuffer(NUM_BYTES_UINT8 + NUM_BYTES_CHAR * 14);
    const outView = new DataView(outBuffer);
    outView.setUint8(0, WebSocketEvent.StringEvent);
    writeStringToBuffer('server says hi', outBuffer, NUM_BYTES_UINT8);
    webSocketServerHandler.send(iws, outBuffer);
});

In examples/binaryHandler/server/, run npm i followed by npm run launch.


Prerequisites

  • Install node and npm
  • Install TypeScript
    • npm i -g typescript
  • Install webpack
    • npm i -g webpack
  • Install webpack-cli
    • npm i -g webpack-cli

Run npm i in examples/<name of example>/server/ to install all npm packages for all required folders (via postinstall).

You can then run npm run launch in examples/<name of example>/server/, as mentioned above.


Production

Before deploying to production, provision your own dedicated ICE/STUN servers and add them to iceServers.ts. By default, STUN servers by Google are included for development:

export const iceServers: RTCIceServer[] = [
    { urls: 'stun:stun1.l.google.com:19302' },
    { urls: 'stun:stun2.l.google.com:19302' },
    { urls: 'stun:stun3.l.google.com:19302' },
    { urls: 'stun:stun4.l.google.com:19302' }
];

Repository Structure

src/ contains the library source code

  • src/
    • client/
      • ts/ contains client-side UDPWebSocket.ts and WebSocketHandler.ts
    • server/
      • ts/ contains server-side UDPWebSocketServer.ts and WebSocketServerHandler.ts

If you want Javascript sources, in src/, run npm run build to compile TypeScript to Javascript in src/client/js/ and src/server/js/. Feel free to use webpack or rollup for browser compatibility (on the client side) and/or minification.


examples/ contains examples using the library source code

  • examples/
    • barebones/
      • client/
        • ts/ contains a client-side example using UDPWebSocket.ts
      • server/
        • ts/ contains a server-side example using UDPWebSocketServer.ts
    • handler/
      • client/
        • ts/ contains a client-side example using WebSocketHandler.ts
      • server/
        • ts/ contains a server-side example using WebSocketServerHandler.ts
    • binaryHandler/
      • client/
        • ts/ contains a client-side example using BinaryWebSocketHandler.ts
      • server/
        • ts/ contains a server-side example using BinaryWebSocketServerHandler.ts

gbz

udp-ws's People

Contributors

regnaio avatar

Stargazers

Chase Carlson avatar Redus avatar Asif Tunga Mubarek avatar Majid Hojati avatar K. S. Ernest (iFire) Lee avatar Benjamin Jurk avatar Paul m. p. Peny avatar Mika Feiler avatar Jean Leao avatar  avatar Caribou (lraymond) avatar Amir .DEV avatar Crosstyan avatar  avatar Alexandre Abidri avatar Joonas avatar Elias Hogstvedt avatar Nuno CP avatar Diwank Singh Tomer avatar  avatar TonicBoomerKewl avatar  avatar a avatar  avatar Mert Yılmaz avatar Joseph Werle avatar  avatar Cael avatar  avatar Finbar Giusti avatar Lungoupao Khongsai avatar Sh1d0w avatar hangxun avatar Kirill Bogomolov avatar Stoney Kang avatar  avatar mywaiting avatar Lucas Costa Gobbi avatar dek avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.