I can't understand why my code (below) doesn't work. The console.log prints the correct socket.id on the server-side, before emitting to the socket. So why isn't the message received?
server-side:
socket.on("connectToUser", function(userName, currentUserName, userID){
console.log("user wants to connect to: ",userID);
socket.to(userID).emit("connectNotification", currentUserName);
});
client-side:
socket.on("connectNotification", function(currentUserName){
console.log("correct user notified");
$("#connectToBox").append("Hallo");
});
Does the socket.id have to be the original one? I am changing it on connection, like this:
socket.on('connection', function(socket){
console.log('a user connected', socket.id);
var id = uuid.v4();
socket.id = id;
console.log(socket.id);
Changing socket.id after the connection event has fired is probably too late, because at that point, socket.io has already done some internal housekeeping based on the original id.
If you want to use your own id's, you should use a middleware function instead (disclaimer: I'm not overly familiar with the internals of socket.io, but some example code I put together seems to work):
let server = require('socket.io')(...);
server.use(function(socket, next) {
socket.id = uuid.v4();
next();
}).on('connection', function(socket) {
socket.on("connectToUser", function(userName, currentUserName, userID){
console.log("user wants to connect to: ",userID);
socket.to(userID).emit("connectNotification", currentUserName);
});
});
Related
I have a simple implementation, or an attempt at one, of a messaging system just to show an alert of a message to different users. I'm using https://www.npmjs.com/package/vue-socket.io Vue Socket Io but it's just a socket.io. I am attempting to have the client subscribe to an event in mounted. The name is based on their userID. The problem is that my implementation below doesn't work to show the alerts. I can see the event being subscribed to in mount and I can see sending message console log in the server so I know that is getting fired but I don't see the alert being triggered by the emit(Chat_....
server:
io.on('connection', function(socket) {
socket.on('sendMessage', function (data) {
console.log("Sending message to" + data.user);
socket.emit(`CHAT_${data.user}`, data.msg)
});
});
client:
On the client, the userIDSelf is a user ID that is passed in as a prop. It's the User's logged in ID and in mounted, they automatically subscribe to the a chat channel with their userid appended to it, like a custom channel.
mounted() {
this.sockets.subscribe(`CHAT_${this.userIDSelf}`, (data) => {
alert(data)
});
},
there is a function sendMessage that takes the values from 2 inputs (not seen) in the template. You have to enter a user ID on who you want the message sent to and then another input with the actual message. These are sent over to the backend server listed above.
methods: {
sendMessage() {
this.$socket.emit('sendMessage', {user: this.userIDReceiver, msg: this.message})
},
}
I see a logical problem here. In your server code
io.on('connection', function(socket) {
socket.on('sendMessage', function (data) {
console.log("Sending message to" + data.user);
socket.emit(`CHAT_${data.user}`, data.msg)
});
});
the socket (User 123) which emitted sendMessage event to the server, will eventually also be the socket which will receive the emitted CHAT_456 event from the server. But User 123 socket only listens to CHAT_123 events. The socket is sending itself a message but doesn't receive it.
Possible Solutions
A - the room approach
Once a socket connects on the server, throw it in a room based on it's user id. This requires sending the userid to the server during connection, e.g. with a query parameter. In the client just add a token query parameter to the connection string like
const io = require('socket.io-client');
const socket = io("https://server-domain.com/?token=" + userid);
and through the connecting socket in a room (join) on the server side like
io.on('connection', function(socket) {
const userid = socket.handshake.query.token;
socket.join(userid);
});
then your socket sendMessage would work like this
/* ... server side */
socket.on('sendMessage', function (data) {
console.log("Sending message to" + data.user);
io.to(data.user).emit('chatMessage', data.msg);
});
B - the keep reference to the socket approach
Internally every socket has a socket id. Normally these are not exposed the user/clients. However you do have a unique user id. So let's keep a user id / socket instance - relation on the server side, so you can get a reference to the socket object for each corresponding user, using the user id.
The most basic example for it would be a in-memory store on the server side
let socketRefs = {};
io.on('connection', function(socket) {
const userid = socket.handshake.query.token;
socketRefs[userid] = socket;
});
then your socket sendMessage would work like this
/* ... server side */
socket.on('sendMessage', function (data) {
console.log("Sending message to" + data.user);
let targetSocket = socketRefs[data.user];
targetSocket.emit('chatMessage', data.msg);
});
So I'm trying to emit data from server to one specific client upon connecting (this is server code):
io.sockets.on('connection', function(socket){
socket.id = Math.random();
//this works, but sends data to all clients, which is not what I need
socket.emit('serverMsg');
//this seems to do nothing
io.to(socket.id).emit('serverMsg');
//this also does nothing
socket.broadcast.to(socket.id).emit('serverMsg');
});
As I've commented the lines, I can emit data to all clients, but nothing happens when I try to emit to specific client with socket.id.
What am I doing wrong? There are no error messages or anything.
When a client connects, its socket.id is automatically initialized. Also, it joins a room with the same name:
Each Socket in Socket.IO is identified by a random, unguessable,
unique identifier Socket#id. For your convenience, each socket
automatically joins a room identified by this id.
If, for some reason, you wish to redefine the id, you would need to assign the socket to the corresponding room as well, since the to( ) method expects as its argument the name of the room of interest. In the code in your post, you redefine the id, but the association with the corresponding room is not established, thus the emit method emits the data into the "void" (into a room the name of which is equal to the result of Math.random(), but which contains no sockets)...
const customId = ....
socket.id = customId;
socket.join(customId);
io.to(customId).emit(...)
this work for me
const http = require('http').createServer();
const sio = require('socket.io')(http);
var port = xxxx;
var ip = "xxx.xxx.x.x";
sio.on('connection', async function(socket) {
sio.sockets.to(socket.id).emit("from_server", "your are connected");
});
http.listen(port, ip, function() {
console.log('Server active. listening on :' + ip + ":" + port);
});
I would like to check in client side if a socket id is still connected.
The version of socket.io I use is <script src="/socket.io/socket.io.js"></script>.
I have tried answers in several threads, but it did not work.
Edit 1: Actually, I have 1 server which can serve many clients. In the server I have the following code which sends the socket id to a client when it is connected:
io.sockets.on('connection', function (socket) {
console.log("LOG: just connected: " + socket.id);
socket.emit('id', socket.id);
socket.on('disconnect', function () {
console.log("LOG: just disconnected: " + socket.id)
})
})
Two clients may talk to each other via the server, that's why ClientA may (besides its own socket id) keep the socket id of ClientB. Thus, it will be useful for me to check (from an id) in the client-side if a client is still connected. It will be OK if this check needs to ask the server, I just want this check to be as simple and sure as possible.
Apparently (from your comments), clientA has a socket.id of clientB and clientA wants to know if clientB is currently connected. Since a socket.id is just a string value, there is no way for clientA to tell from just the socket.id if clientB is connected or not. That information would have to come from the server.
You can have clientA ask the server if it thinks clientB is connected (by sending the socket.id of clientB to the server in a request message) and the server could respond with what it knows about clientB's connected state.
The server maintains a list of all connected clients. One of the ways you can access that list is via a map that is indexed by socket.id. So, it would be easy for the server to see if a connected client is in the map with a given socket.id.
Here's a way you could ask your server if a given socket.id is connected. This would be your server code:
io.sockets.on('connection', function (socket) {
console.log("LOG: just connected: " + socket.id);
socket.emit('id', socket.id);
socket.on('disconnect', function () {
console.log("LOG: just disconnected: " + socket.id)
})
// use the ack function argument to send a response back
socket.on('isConnected', function(id, ackFn) {
let otherSocket = io.sockets.connected[id];
ackFn(!!otherSocket && otherSocket.connected);
});
});
And, the client would send a request and listen for the response:
socket.emit('isConnected', someId, function(result) {
console.log(someId + ": " + result ? "is connected" : "is not connected");
});
Original answer that thought you were asking how a client can tell if its own socket is connected.
A socket.io object has a .connected property that reflects what socket.io thinks is the current state of the connection.
if (socket.connected) {
// socket.io thinks it is still connected
}
You can see it being used here internally in the .emit() method:
if (this.connected) {
this.packet(packet);
} else {
this.sendBuffer.push(packet);
}
Socket.io already implements its own heartbeat sent from server to client so it will usually notice when the connection stops working. By default, it will try to reconnect, but if that reconnection does not work immediately, the socket may remain in the disconnected state while it retries further reconnects.
I suggest sending a broadcast every 30 sec or so, This way you won't loose connection to your socket.
window.setInterval(function(){
yoursocket.emit('broadcast');
}, 30000 /* 30 sec */);
This may seem odd but I have a situation where I'm keeping an array of uses in io.on('connection', function(socket) { ... }); scope for each user currently connected to a room. So when a user disconnects, I need to update that array on the server. The issue is, since this array is unique to each socket/user I can't just update the array directly.
So what I'm doing is broadcasting an update to all users then immediately having users emit a message back. Is there a cleaner method of doing this?
Quick and dirty example:
// server.js
io.on('connection', function(socket) {
var usersArray = getUsersArray();
socket.on('user array update', function() {
usersArray = removeUser(user);
});
socket.on('disconnect', function() {
socket.broadcast.to(room).emit('user disconnected', user);
});
});
//client.js
socket.on('user disconnect', function(user) {
socket.emit('user array update', user);
});
I'm new to node.js but this ping pong type communication feels a bit unnecessary, is this pretty standard or is there a better way I should be doing this?
i am trying to get a chat program to work using socket.io but it doesnt seem to work properly.
i am using a Node.js server and it seems to be running properly. i think this may have something to do with emitting to rooms.
the code i have on the client browser is:
<script>
var socket = io("https://localhost:3000/userChat");
socket.on('connect', function(){
socket.emit('initialiseConnection', "user1");
});
socket.on('messageIn', function(msg){
console.log(msg);
$('#messages').append($('<li>').text(msg.message));
});
</script>
so when the page loads, socket.io it connects to the server and emits the "initialiseConnection" event on the server with "user1". which is a new room specifically for that user.
on the server, the code i have handling "initialiseConnection" is:
socket.on("initialiseConnection", function(username){
socket.join(username);
console.log(username + " has joined.");
Message.find({recipient:username}, function (err, message){
console.log("messages for "+username+": "+message.length);
for (var x = 0; x < message.length; x++){
console.log(username+"'s message "+x);
console.log(message[x]);
socket.to(username).emit("messageIn", {"message":message[x]});
}
});
});
this code as you can see, creates and joins a room with the same name as the username. the looks in the database for any messages, and tries to emit those messages to the user. i log the message. there is definately a message and the username in the "socket.to()" method is also correctly shown in the logs. but the "socket.on('messageIn')" on the client browser doesnt seem to be picking up the event.
i have also tried putting:
setTimeout(function() {
socket.to(username).emit("messageIn", {"message":"test message"});
}, 5000);
immediately after the socket.join(), in case this was related to some backbround processing that needed to complete
can anyone see where i may have gone wrong on this?
Thanks.
--EDIT 1--------------------------------------
var https = require('https');
var express = require('express');
var app = express();
var server = https.createServer(https_options, app).listen(3000);
var io = require('socket.io')(server);
var userChat = io.of("/userChat");
userChat.on('connection', function(socket){
console.log('a user connected');
socket.on("initialiseConnection", function(username){
...
});
socket.on('disconnect', function(){
socket.leave(socket.room);
});
});
You need to change this:
socket.to(username).emit("messageIn", {"message":message[x]});
to this:
socket.emit("messageIn", {"message":message[x]});
A socket is the endpoint. When sending, you just send directly to the socket. You don't send to a user in a room? You just send to a socket. Since you already have the socket in your socket variable, that's what you send to.