We have already sent data in both directions in earlier examples. In this post, we slow down and look specifically at the different ways data can flow, since Socket.IO gives you more options than a single client just talking to a single server.
The Four Basic Directions
Every real Socket.IO application uses some combination of these four patterns:
- Client sends data to the server
- Server sends data back to that one specific client
- Server sends data to every connected client
- Server sends data to every client except the sender
Let's go through each one with real code.
1. Client to Server
This is the simplest direction, the client emits an event, and the server listens for it.
Client:
socket.emit("sendMessage", { text: "Hello", sender: "Gagan" });
Server:
socket.on("sendMessage", (data) => { console.log(data.text, data.sender); });
Inside the connection handler, socket always represents that one specific client's connection, so anything you do with socket only affects that particular user.
2. Server to One Specific Client
If the server wants to reply only to the client that just sent something, it uses socket.emit(...) from inside that same connection handler:
io.on("connection", (socket) => { socket.on("sendMessage", (data) => { socket.emit("messageReceived", { status: "delivered" }); }); });
Since socket refers to one exact connection, socket.emit(...) here only reaches that single client, nobody else on the server gets this event.
3. Server to Every Connected Client
Sometimes you want everyone to see an update, for example, a new chat message that all users should see. For this, you use io.emit(...) instead of socket.emit(...):
io.on("connection", (socket) => { socket.on("sendMessage", (data) => { io.emit("newMessage", data); }); });
Here, io represents the entire server, not one connection. io.emit(...) sends the event to absolutely every client currently connected, including the one who originally sent the message.
4. Server to Everyone Except the Sender
Often, you don't want to send an update back to the same person who triggered it, they already know, since they're the one who did it. For this, Socket.IO gives you socket.broadcast.emit(...):
io.on("connection", (socket) => { socket.on("sendMessage", (data) => { socket.broadcast.emit("newMessage", data); }); });
This sends the event to every client except the one represented by socket. This is extremely common in chat apps, you show the sender their own message instantly through local state in React, and use broadcast so the server does not also send it back to them separately.
A Very Common Real Pattern
Combining points 2 and 4 together is one of the most useful patterns you will use repeatedly:
socket.on("sendMessage", (data) => { socket.emit("messageSent", { status: "ok" }); socket.broadcast.emit("newMessage", data); });
Here, the sender gets a private confirmation that their message was received, while everyone else gets the actual new message. This avoids duplicate messages appearing for the sender, while still keeping everyone in sync.
Sending to a Specific Client by ID
Sometimes you don't have direct access to a client's socket object, but you know their socket ID (perhaps you stored it earlier, tied to a user account). In that case, you can target them directly:
io.to(socketId).emit("privateNotification", { text: "You have a new order" });
This sends the event only to the client with that exact socket ID, useful for private notifications, direct messages, or admin actions targeting a specific user.
A Quick Reference Table
|
Code |
Who
Receives It |
|
socket.emit(...) |
Only this one
client |
|
io.emit(...) |
Every
connected client, including sender |
|
socket.broadcast.emit(...) |
Every client
except the sender |
|
io.to(socketId).emit(...) |
Only the
client with that specific socket ID |
Client Side: Listening for Both Kinds of Events
On the client, you don't need to know or care which method the server used, you just listen normally:
useEffect(() => { function onMessageSent(data: { status: string }) { console.log("My message status:", data.status); }
function onNewMessage(data: { text: string; sender: string }) { console.log("New message from someone:", data); }
socket.on("messageSent", onMessageSent); socket.on("newMessage", onNewMessage);
return () => { socket.off("messageSent", onMessageSent); socket.off("newMessage", onNewMessage); }; }, []);
Why This Matters for Real Apps
Understanding these four directions properly prevents two very common beginner mistakes:
- Using
io.emit(...)everywhere, which accidentally sends private data (like personal notifications) to every connected user instead of just one - Using
socket.emit(...)for something meant to update everyone, which means other users never see the update in real time at all
Choosing the right one of these four patterns for each event is really the core skill of designing a Socket.IO application properly.
Summary
socket.emit(...)sends data to only the one client tied to that connectionio.emit(...)sends data to every connected client, including the sendersocket.broadcast.emit(...)sends data to every client except the sender, extremely common in chat-style featuresio.to(socketId).emit(...)targets one specific client by their socket ID- Picking the correct direction for each event is essential to avoid data going to the wrong people, or not reaching the right people at all
In the next post, we cover handling connection and disconnection events properly, including cleanup logic and tracking online users.
No comments:
Post a Comment