Using Web Sockets
benlongcp
Posted on November 18, 2024
Maybe you're a little kid and you're visiting your relatives one summer. Maybe they have a basement that has been converted into a game room for your cousins. Maybe there's a tv and n64, and a pool table. And a ping-pong table.
When you're a little kid, your hand-eye coordination is mostly not great, but you really want to play some ping-pong, and since it's not often your cousins have fresh meat to play against you end up going for a few heats.
After summarily demonstrating their proficiency, they perhaps take pity on you or just become bored watching you scramble after the ping-pong ball time and again. No longer do they take Olympian slap shots, instead now, the game becomes something more tranquil, steady. It becomes about keeping the ball bouncing on the table, a steady rhythm willfully exchanged between two cooperative individuals.
This reflexive exchange of an object bears much resemblance to a mechanism in client-server architecture that allows users to exchange data with one-another in real time.
Web sockets are such a tool that allow for real-time communication between two or more separate clients connected to a server.
In a standard case, we might have two clients trying to chat with one-another. If the first client sends a message to a server rest API, the server receives the post request, and then how does the message reach the second client?
The second client must make periodic get requests to the server in order to eventually receive the first client's post. This creates the added problem of latency, in that the second client could send a message before the first client's message arrives.
Using web-sockets, on the other hand, allows for a continuous real-time connection and exchange of information. Instead of the second client having the burden of sending intermittent get requests to the server, the server itself assumes responsibility for receipt of the message and then broadcasting it to any other client to which it has an open connection.
Much like trying to keep a ping-pong ball in-play with a 6-year-old, an affirmative connection and intent must be established. The client sends the ball of information to the server who in turn ping-pongs it back to another affirmatively-connected client.
In web socket parlance, this affirmative connection is known as a handshake. Rather than with a typical get request that is finite and periodic, the handshake is a continuous open channel for information to flow and be redirected.
Practically speaking, using websockets is a combination of back-end server-side code, as well as on the client. In these examples, the library socket.io is used.
The necessary dependencies are as follows:
//after installing dependencies, import into the server
import express from 'express';
import http from 'http'
import cors from 'cors'
import { Server } from 'socket.io'
Here, we create an express instance and apply middleware
//start express instance
const app = express();
//cors is used for middleware
app.use(cors())
The websockets need to use an http server for connections, so upon the express server, we create an http server instance
//makes an http server with the express server
const server = http.createServer(app)
//creates an io server using the http server made with the express server
const io = new Server(server, {
//some kind of cors options object, idk
cors: {
origin: "http://localhost:8080",
methods: ["GET", "POST"],
credentials: true,
},
})
Now we define behavior for what to do with information from the clients connected to the server:
//when the server establishes a connection, it shall do the following:
io.on('connection', (socket)=>{
//if it receives data marked send_message
socket.on('outgoing_message', (data)=>{
//it shall re broadcast that message back to the client
socket.broadcast.emit("incoming_message", data)
})
})
Finally at the bottom, we ensure the instance of the http server on the express server is listening.
//The http server listens at this port
server.listen(8080, ()=>{
console.log("SERVER RUNNING MARATHON")
})
Now on the client side, say in a react component, we ensure we have installed and import the following:
import React from 'react';
import { io } from "socket.io-client";
import { useState, useEffect } from 'react';
With io we can connect to the server:
//creates front-end socket connection to the server
const socket = io("http://localhost:8080");
Our component would might look something like this:
export default function Messager(){
//creates react hooks for messages sent and received
const [messageOut, setMessageOut] = useState("")
const [messageIn, setMessageIn] = useState("")
//sends a message over the socket
const sendMessage = () =>{
//sends the message state
socket.emit("outgoing_message", { messageOut })
}
//when the client socket receives a new message, the incoming message state is updated
useEffect(()=>{
socket.on("incoming_message", (data)=>{
setMessageIn(data.messageOut)
})
}, [socket])
//renders an input a button, and a spot for messages
return (
<div>
<h1>hellooooo there</h1>
<input placeholder='message' onChange={(e)=> setMessageOut(e.target.value)}/>
<button onClick={sendMessage}>send message</button>
<h1>log:</h1>
{messageIn}
</div>
)
}
Visiting the client in a browser will result in the world's most basic messaging app. We input data, that data is sent to the server, the server precipitates our sent data into a broadcast message, so any other open connection to the server will receive the sent message. This, however, lays the groundwork for the capability for users to interact with each other in multitudes of ways, and to do so in real time.
Posted on November 18, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.