Server side
Features
- Express.js Application Setup with CORS enabled, HTTP server object to serve the Express.js application
- Event Handling for Disconnections
- Room Selection Handling
- Connection Event Listening
- Connection Error Handling
Explanations
apps\backend\src\index.ts
Create an Express.js application with CORS enabled, running on a specified port and served by an HTTP server:
const app = express();
const port = env.port;
app.use(cors());
// create a HTTP server object
const server = http.createServer(app);
Initialize a new instance of socket.io:
const io = new Server(server, {
cors: {
origin: env.clientUrl,
// credentials: true
}
});
Define the socket object type:
type SocketType = Socket<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, Record<string, never>>;
Declare custom rooms (an arbitrary channel that sockets can join
and leave
):
const customRooms = ["Room A", "Room B", "Room C"];
Event handler for managing disconnections. Use io.of("/").sockets.size
to get The number of currently connected clients, and emit the receive_online_people_count
event to all connected clients, including the current one:
const handleDisconnect = (socket: SocketType) => () => {
console.log('A user disconnected:', socket.id, 'io.of("/").sockets.size:', io.of("/").sockets.size);
try {
io.emit("receive_online_people_count", io.of("/").sockets.size);
} catch (error) {
console.error(error)
}
}
A generic function to handle socket emit events: check if a client has joined an additional room beyond the default room (public channel) using socket.rooms.size > 1
; if so, emit the event to that specific room, otherwise, emit the event to all connected clients, including the current one:
const handleEmit = <T extends Record<string, unknown> | string | number | boolean>(socket: SocketType, eventName: string) => (value: T) => {
try {
// NOTE: `socket.rooms.size > 1` because a socket is always in the public channel
if (socket.rooms.size > 1) socket.rooms.forEach(room => socket.to(room).emit(eventName, value)); // TODO: `socket.to(room).emit` this is not able to send to the sender itself
else io.emit(eventName, value);
} catch (error) {
console.error(error)
}
}
Handle the clientβs "choose room" event by defining a leaveRooms
function that allows the current socket to leave all custom rooms (while remaining in the default public channel), and emit the receive_room_selected_value
event with the rooms the current socket joins to the socket itself:
const handleSelectRoom = (socket: SocketType) => (roomId: string) => {
console.log('user enter the room:', socket.id, `roomId: ${roomId}`);
const leaveRooms = () => {
socket.rooms.forEach(room => { if (customRooms.includes(room)) { socket.leave(room) } });
}
try {
leaveRooms(); // NOTE: allow this socket to join only one room at a time
if (roomId !== "public channel") socket.join(roomId);
socket.emit("receive_room_selected_value", roomId, `socket: ${socket.id} is in the rooms: ${Array.from(socket.rooms).join(',')}`);
} catch (error) {
console.error(error)
}
}
Listen for the connection event to handle incoming sockets, then use io.emit("receive_online_people_count", io.of("/").sockets.size);
to broadcast the current number of connected clients:
io.on('connection', (socket: SocketType) => {
console.log('a user connected', socket.id);
io.emit("receive_online_people_count", io.of("/").sockets.size);
socket.on("send_msg", handleEmit(socket, 'receive_msg'));
socket.on("dropdown_selected_value", handleEmit(socket, 'receive_dropdown_selected_value'));
socket.on("checkbox_is_checked", handleEmit(socket, 'receive_checkbox_is_checked'));
socket.on("radio_selected_value", handleEmit(socket, 'receive_radio_selected_value'));
socket.on("textarea_value", handleEmit(socket, 'receive_textarea_value'));
socket.on("map_position", handleEmit(socket, 'receive_map_position'));
socket.on("room_selected_value", handleSelectRoom(socket));
socket.on("disconnect", handleDisconnect(socket));
});
Listen to connection errors and handle them:
io.engine.on("connection_error", (err) => {
console.log(err.req); // the request object
console.log(err.code); // the error code, for example 1
console.log(err.message); // the error message, for example "Session ID unknown"
console.log(err.context); // some additional error context
});
Expose the port:
server.listen(port, () => {
console.log(`Listening on port ${port}...`);
});
π Check out the code on GitHub
Top comments (0)