- TCP and require a handshake to establish connection
- realtime communication tech (chat, multiplayer games etc.)
- remember to implement heartbeats to maintain connections
- socket.io is a great library that abstracts sockets across programming languages, but they're easy to use in JS without additional libraries.
Two options for dealing with ws: ws - A node package for creating servers that understand the WebSocket protocol express-ws - A node package for handling WebSocket routes using standard Express APIs.
We'll use ws... just because that's what I'm more familiar with. express-ws looks like a great option though!
To setup the project:
- Run
npm init viteand make a new Svelte + JavaScript project. cdinto the new directory, and runnpm installto get all the Vite dependencies- Get our express / websocket dependencies:
npm install express vite-express ws
In order to get our setup to work with the Vite dev server, we have to bind ViteExpress to our http server instead of having ViteExpress do the listening itself. Create a server.js file in the top level of your project directory and paste the following server code inside:
/*
1. Open up a socket server
2. Maintain a list of clients connected to the socket server
3. When a client sends a message to the socket server, forward it to all
connected clients
*/
import express from 'express'
import http from 'http'
import ViteExpress from 'vite-express'
import { WebSocketServer } from 'ws'
const app = express()
const server = http.createServer( app ),
socketServer = new WebSocketServer({ server }),
clients = []
socketServer.on( 'connection', client => {
console.log( 'connect!' )
// when the server receives a message from this client...
client.on( 'message', msg => {
// send msg to every client EXCEPT the one who originally sent it
clients.forEach( c => { if( c !== client ) c.send( msg ) })
})
// add client to client list
clients.push( client )
})
server.listen( 3000 )
ViteExpress.bind( app, server )Replace all the code in your App.svelte component with the following:
<script>
let msgs = []
const ws = new WebSocket( 'ws://127.0.0.1:3000' )
// when connection is established...
ws.onopen = () => {
ws.send( 'a new client has connected.' )
ws.onmessage = async msg => {
// add message to end of msgs array,
// re-assign to trigger UI update
const message = await msg.data.text()
msgs = msgs.concat([ 'them: ' + message ])
}
}
const send = function() {
const txt = document.querySelector('input').value
ws.send( txt )
msgs = msgs.concat([ 'me: ' + txt ])
}
</script>
<input type='text' on:change={send} />
{#each msgs as msg }
<h3>{msg}</h3>
{/each}In the root level of your Vite project, run npm run dev. In a separate terminal window, go ahead and start your new server with node server.js. With the Vite development server running, open up the app by pointing your browser to http://localhost:PORT, where PORT is the port number Vite gave you when you ran npm run dev. If you open the Svelte app in two different tabs / browsers you'll be able to see that you can send messages back and forth. Realtime communication is ready to go.
There are lots of applications where a simple relay server can be useful with WebSocket messages. For example, we can use the exact same server to make a simple “drawing” application. We’ll create a <canvas> object that fills the entire window, and then send the current mouse position whenever the window is clicked. We’ll then add code to take any message received and draw a box at the provided location.
To run this example, clear your App.svelte file and replace the contents of index.html with the code below:
<html lang="en">
<head>
<style>
body {
margin:0;
background:black
}
</style>
<script>
let ws, msgs = [], ctx = null
window.onload = function() {
ws = new WebSocket( 'ws://127.0.0.1:3000' )
ws.onopen = () => {
ws.onmessage = async msg => {
const pos = await msg.data.text()
const [x,y] = pos.split( ':' ).map( v => parseInt(v) )
ctx.fillStyle = 'red'
ctx.fillRect( x,y,50,50 )
}
}
const canvas = document.querySelector('canvas')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
ctx = canvas.getContext( '2d' )
window.onclick = e => {
ws.send( `${e.pageX}:${e.pageY}` )
ctx.fillStyle = 'yellow'
ctx.fillRect( e.pageX,e.pageY,50,50 )
}
}
</script>
</head>
<body>
<canvas></canvas>
</body>
</html>