I am using Socket.io to connect a React client to a Node.js server and the query option in socket.io to identify uniquely every new client. However, the server creates multiple sockets for every client and, when I need to send something from the server, I don't know which socket use, because I have more than one, and all of them are connected.
The client code:
import io from "socket.io-client";
...
const socket = io(process.env.REACT_APP_API_URL + '?userID=' + userID, { forceNew: true });
socket.on('connect', () => {
socket.on('new-order', data => {
const { add_notification } = this.props;
add_notification(data);
});
The server code:
....
server = http
.createServer(app)
.listen(8080, () => console.log(env + ' Server listening on port 8080'));
io = socketIo(server);
io.on('connection', socket => {
const userID = socket.handshake.query.userID;
socket.on('disconnect', () => {
socket.removeAllListeners();
});
});
And here the server-side that emits events to the client:
for (const socketID in io.sockets.connected) {
const socket = io.sockets.connected[socketID];
if (socket.handshake.query.userID === userID) {
// Here, I find more than one socket for the same condition, always connected.
socket.emit(event, data)
}
}
Here, it is possible to see all these socket for the same client:
I tried to send events for all socket from a given userID, however, multiple events are triggered to the client, showing duplicated data to the user. I also tried to send events to the last socket, but, sometimes it works, sometimes doesn't.
Someone have a clue how to uniquely identify a socket when there are several clients?
Related
I have a chat app and currently, the client is saving its name in a text file, this works fine for windows but mac has some weird directory settings so it makes it harder to read the text file. I'm wondering if it's possible that when a client connects my server saves their IP or some sort of constant data about the client so when the client connects again I can know who it is and assign the name accordingly.
I'm using nodeJS socket.io
First thing that comes to my mind is to make the client responsible for identifying itself by sending a generated UUID to server after connecting
socket.on("connect", () => {
const CLIENT_UUID = 'uuid';
// Get client UUID from local storage
let clientUuid = localStorage.getItem(CLIENT_UUID);
// Check whether if this is a new client and it doesn't have UUID
if (!clientUuid) {
// Then generate random UUID, you'll need to implement `generateRandomUuid`
clientUuid = generateRandomUuid();
// Then save it to local storage
localStorage.setItem(CLIENT_UUID, clientUuid);
}
// Then just emit the connected event
// This will be the actual connection event as once this is emitted and received
socket.emit('connected', {
uuid: clientUuid
});
});
Now on server you can handle the client based on it's UUID
io.on('connection', (socket) => {
// Here you handle the `connected` event
socket.on('connected', (clientUuid) => {
// And now you can handle this clientUuid
const chats = getCurrentClientChats(clientUuid);
const groups = getCurrentClientGroups(clientUuid);
socket.emit('current-chats', { chats });
socket.emit('current-groups', { groups });
});
});
Notice that way, you'r client is now actually known on the connected event
Same approach but probably cleaner is to send the client UUID upon connecting to the server, since it's really easy to do that with socket IO
const socket = io("ws://example.com/my-namespace", {
query: { clientUuid }
});
And just use it upon connection
io.on('connection', (socket) => {
const clientUuid = socket.handshake.query.name;
const chats = getCurrentClientChats(clientUuid);
const groups = getCurrentClientGroups(clientUuid);
socket.emit('current-chats', { chats });
socket.emit('current-groups', { groups });
});
It's been a while since I've worked with Node and Websockets. Basically how do I get socket.send() to work from another function is what I'm stuck on.
const server = new WebSocket.Server({ port: 8080 });
server.on('connection', socket => {
socket.on('message', message => {
console.log(`received from a client: ${message}`);
});
socket.send('yo world!');
});
function onMessageHandler (target, context, msg, self) {
client.say(target, response);
server.socket.send(response);
console.log(response);
}
}
How do I get my onMessageHandler to trigger a socket send, this is fail... server.socket.send(response);
Seeing your question i think there is a lack of understanding on how Websockets work. I am assuming you're using https://github.com/websockets/ws
There are two things. First is the WebSocketerver which you've named as server and then an Individual Socket which you've named as socket
Now the thing to understand is socket is not accessible outside server.on() callback The reason for this is there could be 1000 of sockets connected at a given instance and there would be no way to uniquely identify a particular socket you want to send message to.
So ask yourself the question that your application wants to send message to an individual socket to send to everyone who is connected to your server (basically broadcast)
If you want to send to an individual, you will have to uniquely identify the user
this._wss = new WebSocket.Server({
port: ENV_APP_PORT_WS
});
this._wss.on("connection", async (ws: AppWebSocket, req: IncomingMessage) => {
// const ipAddress = req.connection.remoteAddress; // IP Address of User
logger.info(req);
const queryParams = url.parse(req.url, true).query;
let authUser: User;
try {
authUser = await this._authenticateWebSocket(queryParams);
} catch (e) {
// Terminate connection and return...
}
// WS User INIT
ws.isAlive = true;
ws.userId = authUser.id;
ws.uuid = Helpers.generateUUIDV4();
ws.send(JSON.stringify({
type: "connected",
env: ENV
}));
});
The above code will add a property to each socket object that will enable it to uniquely identify a particular user/socket.
While sending =>
onMessageHandler(targetUserId: number, message: string) {
const allSockets = <AppWebSocket[]>Array.from(this._wss.clients.values());
const targetSocket = allSockets.find(w => w.userId === targetUserId);
targetSocket.send(message);
}
If You want to send to all connect users, it's quite easy:
https://github.com/websockets/ws#server-broadcast
Scenario is i have a server where socket(1) runs i have one more server where socket(2) client connects to socket(1)
I have one browser socket which connects to socket(1)
Idea is to do request from browser and bring data from socket(2) server
Not sure how to difference between socket clients as all the sockets are similar to socket(1)
Ideally there will be multiple browser sockets and multiple socket(2) clients
Browser sockets can make request to any of the socket(2) clients
How to implement it using nodejs socket.io
Server
socket.on('action', (action) => {
if(action.type === 'server/hello'){
io.sockets.emit('broadcast',{ description: clients + ' clients connected!'});
console.log('Got hello data!', action.data);
}
});
Browser client
var socket = io.connect('localhost:3000', {reconnect: true});
socket.on('connect', function(data) {
socket.emit('joined', 'Hello World from client this is client plxx');
});
socket.on('response2', function(data) {
console.log("got it ", data);
$('#messages').append($('<li>').text(JSON.stringify(data)));
});
Server client
var io = require('socket.io-client');
var socket = io.connect('http://localhost:3000', {reconnect: true});
socket.on('broadcast', function (t) {
socket.emit("data", {data: 32})
console.log('broadcast! my host is est');
});
i should be able to communicate between socket clients
What I understood from your question is: you need to differentiate between sockets from different clients.
To solve that I would suggest simply emitting the socket source from the client on connect.
And on the server split the sockets into two lists.
Example:
Server
const BROWSER_CLIENTS = {};
const SERVER_CLIENTS = {};
io.on("connection", socket => {
socket.on("source", payload => {
if (payload == "browser")
BROWSER_CLIENTS[socket.id] = socket;
else if (payload == "server")
SERVER_CLIENTS[socket.id] = socket;
});
socket.on("disconnect", () => {
delete BROWSER_CLIENTS[socket.id];
delete SERVER_CLIENTS[socket.id];
});
});
Browser Client
socket.on("connect", () => {
socket.emit("source", "browser");
});
Server Client
socket.on("connect", () => {
socket.emit("source", "server");
});
Now when you receive an event you can detect from which source it originated. And if you need to send to all sockets of one type of clients you can simply do this:
Server
for (let i in BROWSER_CLIENTS)
BROWSER_CLIENTS[i].emit("Hello Browsers")
for (let i in SERVER_CLIENTS)
SERVER_CLIENTS[i].emit("Hello Servers")
EDIT: I found this link and thought you could make use of it. Socket.io Rooms
The nodejs (10.16) and express (4.16) backend server (server) runs with socket.io 2.2.0 for a chat app. The message received on the server will be broadcast to the clients connecting to the same room (channel). The socket.io instance is created in index.js:
const io = require('socket.io')(server);
io.on('connection', async (socket) => {
socket.my_id = "";
console.log("socket.id : ", socket.id);
app.set("socket", socket);
console.log("Socketio server is initialized");
//disconnect
socket.on('disconnect', async function() {
//if (socket.my_id) helper.RemoveSocketUser(user.fort_token, socket.my_id);
console.log('disconnected event');
});
});
server.listen(port, () => {};
Then after a message is saved on the server, the socket is used to broadcast to the clients in the same room.
try {
await msg.save();
msg.user_name = req.user.name;
msg.user_avatar = req.user.user_data.avatar;
const io = req.app.get("io");
const socket = req.app.get('socket');
const room = msg.event_id.toString();
socket.my_id = req.user.id;
socket.join(room);
console.log("socket in new message : ", socket.id);
console.log("msg before broadcast : ", msg.data.msg_body);
io.to(room).emit("event message", msg);
return res.status(200).send(req.body.data.msg_body);
} catch (err) {
console.log("Error in saving/braodcasting a new message", err);
return res.status(400).send(err.message);
};
From the console output, the socket.id is the same for 2 clients connecting to the same room and chatting. Here is a output for Android emulator running on port 8081:
socket in new message : b0po9l6hGZUi9xVDAAAE
msg before broadcast : 8081
Here is another android emulator running on port 9090:
socket in new message : b0po9l6hGZUi9xVDAAAE
msg before broadcast : 9090
However the socket.id on clients side is not the same. What causes the server using the same socket.id for 2 clients?
What you are trying to do is accessing socket object stored in express,
which will update socket object every time new socket get connected that's why you are getting same socket id [ last connected ].
to access specific socket from outside the io connection scope, you have to get the socket instance from io object.
you need to store socket id somewhere in database or memory like redis.
const socketid = `get user's latest connected socket id.`
cont socket = io.sockets.sockets[socketid];
socket.emit('req', { data: "something"})
I want my client-side code to send the server the user's userid when establishing the connection, then i want the server to check the database for new messages for each user that is connecting, and send the user the number of new messages it has when new messages are available.
My client-side code:
var socket = io.connect('http://localhost:8000');
socket.on('connect', function () {
socket.emit('userid', '1');
});
socket.on('new_message', function (data) {
var number_of_messages= "<p>"+data.number+"</p>";
$('#container').html(number_of_messages);
});
My server-side code:
io.sockets.on( 'userid', function (data) {
console.log('userid: '+data);
});
My problem is that the above code is not working: the userid is never received by the serverside and the on('userid') is never called.
My question is how to know which socket sent this user id and how to send to only this specific socket a certain message.
I have solved the problem by saving the clients socket and their id into a global array. this is not a good solution but it works; I know there are rooms and namespaces but I never used it..
socket.io namespaces and rooms
however,
(I used express)
client:
var socket = io.connect('http://localhost:3000',{reconnection:false});
socket.once('connect', function() {
socket.emit('join', '#{id}');
};
server:
var clients = [];
app.io.on('connection', function(socket) {
socket.on('join', function(data) {
clients.push({
ws: socket,
id: data
});
//retrive the messages from db and loop the clients array
//and socket.send(things)
}
}