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)