I've been experimenting with sockets lately, and I've ran into a bug that I'm not sure why is happening.
I created an index page with a button titled createRoom, which attempts creates a lobby/room in which other users can connect.
//index.js
createBtn.addEventListener('click', () => {
socket.emit("create-room", socket.id)
})
socket.on("redirect-host", roomId=>{
console.log("I JUST CREATED: " + roomId);
window.location.replace("/master") //lobby
})
//server.js
io.on("connection", socket => {
console.log("a user connected");
socket.on("create-room", hostId => { //receives the id of the person creating lobby
let roomId = Math.round(Math.random() * (999999 - 100000) + 100000);
socket.join(roomId)//generates room and then sends emit back to redirect host
console.log("Socket " + hostId + " created room: " + roomId);
socket.to(hostId).emit('redirect-host', roomId);
})
})
In index, there is a button you can click to generate a room which emits a create-room to server, which then creates a room and emits the room number back and tells server.js to redirect to a lobby screen (/master).
However, nothing seems to happen upon button press. My main suspect is that "redirect-host" is being sent to the wrong place, but then again I am new to sockets so I can't be sure of it.
Thank you.
EDIT: Resolved! To anyone wondering, the parameter "socket" is the connected socket. According to the socketio documentation, .to sends the emit to everyone but itself
Instead used,
io.in(roomId).emit('redirect-host', roomId);
Socket stands for the connected socket itself. Looking at the documentation:
// to all clients in room1 except the sender
socket.to("room1").emit(/* ... */);
Instead, I believe you want the server to send something to ALL clients in the room. For that, use io.in
io.in(roomId).emit('redirect-host', roomId);
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);
});
I am working on an online website with real-time chat and I've been trying to set up socket.io rooms for a few days now, but one of my custom events just doesn't emit.
My website has 2 paths, / and /play. When someone fills out a form in the / path, the website redirects them to the /play path.
const io = require('socket.io')(listener);
io.on('connection', socket => {
socket.on('add user', data => { // This event gets emitted when the user submits the form at `/`, in a client-side js file.
socket.username = data.username;
socket.room = data.room;
socket.score = 0;
socket.join(data.room);
io.sockets.in(data.room).emit('user joined', { // This event gets 'handled', in another (not the same one) client-size js file.
username: data.username,
room: data.room
});
});
socket.on('disconnect', function () {
io.sockets.in(socket.room).emit('user left', {
username: socket.username,
room: socket.room
});
socket.leave(socket.room)
});
});
In the other client-side js file:
var socket = io.connect();
let joined = 0;
socket.on('user joined', data => {
joined++;
console.log(joined, data)
const elemental = document.createElement("LI");
const info = document.createTextNode(data.username);
elemental.appendChild(info);
document.getElementById('people').appendChild(info)
});
The add user event code executes just fine, but the user joined one doesn't..
I'm almost certain that it has to do with the paths. I read on socket.io namespaces but it seems like they are just like a different type of rooms.
Can someone tell me what's wrong?
EDIT: I should mention that no errors come up.
Without seeing your whole code, I'm going to assume that the socket on client-side.js code, is not joined to the data.room room, and that's why you don't get anything on the 'user join' event listener and that happens because you probably have multiple socket.io connections on the client side code.
You have multiple ways to solve this, depending on what you're trying to achieve.
Instead of only emitting to people on the room, emit to all sockets that an user joined a specific room. io.emit('user joined') instead of io.sockets.in(data.room)...
There should only be one single var socket = io.connect(); on the front end, otherwise the socket that's emitting: add user and therefore joined to data.room is not the same socket that's listening on: user joined, and that's why you never get the event on that socket, because you have 2 different sockets, one that joined the room, and one that is doing absolutely nothing except waiting for an event that will never occur.
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 */);
I'm trying to implement private messaging in an app im creating using express 3 and socket.io
When a client connects a room with the clients userid is automatically created and joined. This is mainly for notifications and that sort of stuff. Now im trying to make this work for private chat too.When a user clicks the send button, the message gets sent along with the userid of the sender (from session userid) and the userid of the owner thats grabbed from a hidden field or attribute of element. And the sender joins the room with the owners userid namespace. The problem with this is that when the sender goes to another page or refresh the browser he is diconnected from the room, and doesnt recieve any further messages from the owner. He has to send a new message to rejoin the owners room. Now how do i percist the connection to the owners room? Or am i doing this all wrong? Is there a better or standard way to achieve this?
SERVER:
io.on('connection', function (socket) {
var sh = socket.handshake;
socket.join(sh.session.userid);
socket.on('chat', function (data) {
if (data.user) {
socket.join(data.owner);
}
io.sockets.in(data.owner).emit('chat',
{user: sh.session.user, message: data.message});
});
});
CLIENT:
var socket = io.connect('https://localhost/');
$('#send_message').click(function (e) {
socket.emit('chat',{message: $('.message').val(),
user:$('#user'), owner:$('#owner')} //Get this from hidden fields in chat form );
e.preventDefault();
});
socket.on('chat', function (data) {
$('ol').prepend('<li>' + data.user + '<br />' + data.message + '</li>');
});
Right. Because when you reload the page, the server gets a "client disconnected" message and unsubscribes the socket. The client will need to re-emit a 'chat' message (with same owner id) in order to get back onto the private feed.
One way is to have the client save the owner id in a cookie and recall it on every page load. Alternatively, you could have the server store and recall this info using a session cookie (http://www.senchalabs.org/connect/session.html), which, in essence, is much like the first option.