I followed a different S/O answer to figure out how to communicate to my server that a client disconnected by using
var socket = io.connect(<your_url>, {
'sync disconnect on unload': true });
The problem is, since this is part of the original socket configuration, I can't tell from the server which of my clients actually disconnected. On my client-side, I display a list of usernames for all connected clients, so I need to know which username to remove from that list for the remaining clients.
server-side code that gets triggered when a client closes out is:
socket.on('disconnect', reason => {
console.log('user disconnected', reason);
});
but the "reason" variable turns out to just be a string that says: "transport close" with no information about the actual client that disconnected.
One approach I thought of was, whenever a client disconnects, the server can request a response from all connected clients and use that to send out an updated list every time, but this seems excessive. I'd much prefer to know which client disconnected when they disconnect, and simply broadcast the id of the newly disconnected client, so the other clients are able to update their respective user lists locally. When a new client joins, after all, I broadcast that client's username, so all clients can update locally - I'd like to use the same pattern when a client disconnects.
In short, does anyone know a way to, within the "sync disconnect on unload" configuration of socket.io, also send the client's ID on unload?
Turns out I needed to keep an array in the server of active participants, adding to it every time they connect.
let users = []
socket.on('join', data => {
data.id === socket.id
users.push(data)
})
socket.on('disconnect', reason => {
let user = users.find(u => u.id === socket.id)
// we emit an event from the server, not from a particular socket
io.emit('player disconnected', {
user
}
})
I created an observable for my websocket connection using WebSocketSubject from rxjs. So far so good the server-client communication is working. Now the thing is: I can't distinguish between the origins of the message in my client. I send messages by calling next() on the subject. All subscriptions on the client get those messages too. How can I send messages only to the server instead?
The implementation mainly stems from this article: https://medium.com/factory-mind/angular-websocket-node-31f421c753ff
My code:
socket$: WebSocketSubject<any>;
constructor() {
this.socket$ = WebSocketSubject.create(SOCKET_URL);
this.socket$.subscribe(
(message) => console.log('<-- ' + message),
(err) => console.error('Error on WebSocket:', err),
() => console.warn('Completed!')
);
}
send(message: SocketMessage) {
const tmp: any = {};
tmp.type = message.type;
tmp.payload = message.payload;
// This will be received by the server but also by client subscriptions = bad
this.socket$.next(JSON.stringify(tmp));
}
I found a solution trying to have your described behavior :
RxJS6's WebSocket's documentation states that
Calling next does not affect subscribers of WebSocketSubject - they have no information that something was sent to the server (unless of course the server responds somehow to a message).
Thus, by using RxJS 6 instead of RxJS 5 you should not have the described behavior.
I have an angular application needing to subscribe to a websocket for incoming data.
On the angular side, in a service I have
setContextChannel(channel) {
this.contextWS = new WebSocket(channel);
that = this;
this.contextWS.onmessage = function(event) {
console.log('ws', event.data);
that.patientID = event.data;
};
this.contextWS.onerror = function(event) {
console.log('ws error', event);
}
}
and on the mock server side, I have a typescript node server that creates the socket as follows:
import {Server} from "ws";
var wsServer: Server = new Server({port: 8085});
console.log('ws on 8085');
wsServer.on('connection',websocket => websocket.send('first pushed message'));//////
my question is how to use the wsServer to send messages?
I'm not sure what are you asking about. This line is correct:
wsServer.on('connection',websocket => websocket.send('first pushed message'));
If you want to keep sending messages to all connected clients, you need to either use the property wsServer.clients to send messages to each connected client, or store the references to each connected client (the websocket variable in your code) in an array and then send the messages to each client using forEach().
Take a look at this code sample: https://github.com/Farata/angular2typescript/blob/master/chapter8/http_websocket_samples/server/bids/bid-server.ts
I have created a mqtt node js client. My connection options are as follows.
mqttOptions = {
clientId: '100',
keepAlive: 1000,
clean: false,
reconnectPeriod: '1000',
will: willMessage
};
I disconnected the server and brought it up again, while the client was still running. The client had the logic to publish every 1 second. Though the client was publishing after this reconnect, it was not receiving the message. It was subscribed to its own message topic. Since I set the clean option to be false, should it not subscribe to the topics on the reconnect and start receiving them?
Below is how I'm establishing the connection.
this.client = mqtt.connect(url, mqttOptions);
and below is how I'm subscribing.
this.client.subscribe(topic);
What am I doing wrong here? Please advice.
We faced this issue with EMQ as the broker and with mqtt library for NodeJS. When it was mosquitto as broker, the client reconnects and gets all the messages it had subscribed. But, if it subscribes again, it gets n number of copies of the same message. As per the library document, it is recommended to check for connack and connack.sessionPresent for previous subscriptions.
We subscribed to all events of client and found that offline is the one that is called when the broker goes down. Then the reconnect and close gets called until the broker is up. Hence, here is how we did it. On offline, end the client forcefully and on completion of end, create a new client - the same function that was used to create client:
doConnect() {
this.client = mqtt.connect('mqtt://myhost', this.myOptionsIfAny);
this.client.on('connect', () => {
this.client.subscribe('mytopics');
this.client.on('message', (topic, message) => {
// do processing
});
this.client.on('offline', () => {
this.client.end(true, () => {
doConnect();
});
});
}
clean: 'false',
Should 'false' definitely be a string? I presume it should be a boolean.
I'm working with socket.io and node.js and until now it seems pretty good, but I don't know how to send a message from the server to an specific client, something like this:
client.send(message, receiverSessionId)
But neither the .send() nor the .broadcast() methods seem to supply my need.
What I have found as a possible solution, is that the .broadcast() method accepts as a second parameter an array of SessionIds to which not send the message, so I could pass an array with all the SessionIds connected at that moment to the server, except the one I wish send the message, but I feel there must be a better solution.
Any ideas?
Ivo Wetzel's answer doesn't seem to be valid in Socket.io 0.9 anymore.
In short you must now save the socket.id and use io.sockets.socket(savedSocketId).emit(...) to send messages to it.
This is how I got this working in clustered Node.js server:
First you need to set Redis store as the store so that messages can go cross processes:
var express = require("express");
var redis = require("redis");
var sio = require("socket.io");
var client = redis.createClient()
var app = express.createServer();
var io = sio.listen(app);
io.set("store", new sio.RedisStore);
// In this example we have one master client socket
// that receives messages from others.
io.sockets.on('connection', function(socket) {
// Promote this socket as master
socket.on("I'm the master", function() {
// Save the socket id to Redis so that all processes can access it.
client.set("mastersocket", socket.id, function(err) {
if (err) throw err;
console.log("Master socket is now" + socket.id);
});
});
socket.on("message to master", function(msg) {
// Fetch the socket id from Redis
client.get("mastersocket", function(err, socketId) {
if (err) throw err;
io.sockets.socket(socketId).emit(msg);
});
});
});
I omitted the clustering code here, because it makes this more cluttered, but it's trivial to add. Just add everything to the worker code. More docs here http://nodejs.org/api/cluster.html
each socket joins a room with a socket id for a name, so you can just
io.to('socket#id').emit('hey')
docs: http://socket.io/docs/rooms-and-namespaces/#default-room
The simplest, most elegant way
verified working with socket.io v3.1.1
It's as easy as:
client.emit("your message");
And that's it. Ok, but how does it work?
Minimal working example
Here's an example of a simple client-server interaction where each client regularly receives a message containing a sequence number. There is a unique sequence for each client and that's where the "I need to send a message to a particular client" comes into play.
Server
server.js
const
{Server} = require("socket.io"),
server = new Server(8000);
let
sequenceNumberByClient = new Map();
// event fired every time a new client connects:
server.on("connection", (socket) => {
console.info(`Client connected [id=${socket.id}]`);
// initialize this client's sequence number
sequenceNumberByClient.set(socket, 1);
// when socket disconnects, remove it from the list:
socket.on("disconnect", () => {
sequenceNumberByClient.delete(socket);
console.info(`Client gone [id=${socket.id}]`);
});
});
// sends each client its current sequence number
setInterval(() => {
for (const [client, sequenceNumber] of sequenceNumberByClient.entries()) {
client.emit("seq-num", sequenceNumber);
sequenceNumberByClient.set(client, sequenceNumber + 1);
}
}, 1000);
The server starts listening on port 8000 for incoming connections. As soon as a new connection is established, that client is added to a map that keeps track of its sequence number. The server also listens for the disconnect event to remove the client from the map when it leaves.
Each and every second, a timer is fired. When it does, the server walks through the map and sends a message to every client with their current sequence number, incrementing it right after. That's all that is to it. Easy peasy.
Client
The client part is even simpler. It just connects to the server and listens for the seq-num message, printing it to the console every time it arrives.
client.js
const
io = require("socket.io-client"),
ioClient = io.connect("http://localhost:8000");
ioClient.on("seq-num", (msg) => console.info(msg));
Running the example
Install the required libraries:
npm install socket.io#3.1.1 socket.io-client#3.1.1
Run the server:
node server
Open other terminal windows and spawn as many clients as you want by running:
node client
I have also prepared a gist with the full code here.
Well you have to grab the client for that (surprise), you can either go the simple way:
var io = io.listen(server);
io.clients[sessionID].send()
Which may break, I doubt it, but it's always a possibility that io.clients might get changed, so use the above with caution
Or you keep track of the clients yourself, therefore you add them to your own clients object in the connection listener and remove them in the disconnect listener.
I would use the latter one, since depending on your application you might want to have more state on the clients anyway, so something like clients[id] = {conn: clientConnect, data: {...}} might do the job.
You can use
//send message only to sender-client
socket.emit('message', 'check this');
//or you can send to all listeners including the sender
io.emit('message', 'check this');
//send to all listeners except the sender
socket.broadcast.emit('message', 'this is a message');
//or you can send it to a room
socket.broadcast.to('chatroom').emit('message', 'this is the message to all');
In 1.0 you should use:
io.sockets.connected[socketid].emit();
Whatever version we are using if we just console.log() the "io" object that we use in our server side nodejs code, [e.g. io.on('connection', function(socket) {...});], we can see that "io" is just an json object and there are many child objects where the socket id and socket objects are stored.
I am using socket.io version 1.3.5, btw.
If we look in the io object, it contains,
sockets:
{ name: '/',
server: [Circular],
sockets: [ [Object], [Object] ],
connected:
{ B5AC9w0sYmOGWe4fAAAA: [Object],
'hWzf97fmU-TIwwzWAAAB': [Object] },
here we can see the socketids "B5AC9w0sYmOGWe4fAAAA" etc. So, we can do,
io.sockets.connected[socketid].emit();
Again, on further inspection we can see segments like,
eio:
{ clients:
{ B5AC9w0sYmOGWe4fAAAA: [Object],
'hWzf97fmU-TIwwzWAAAB': [Object] },
So, we can retrieve a socket from here by doing
io.eio.clients[socketid].emit();
Also, under engine we have,
engine:
{ clients:
{ B5AC9w0sYmOGWe4fAAAA: [Object],
'hWzf97fmU-TIwwzWAAAB': [Object] },
So, we can also write,
io.engine.clients[socketid].emit();
So, I guess we can achieve our goal in any of the 3 ways I listed above,
io.sockets.connected[socketid].emit();
OR
io.eio.clients[socketid].emit();
OR
io.engine.clients[socketid].emit();
You can do this
On server.
global.io=require("socket.io")(server);
io.on("connection",function(client){
console.log("client is ",client.id);
//This is handle by current connected client
client.emit('messages',{hello:'world'})
//This is handle by every client
io.sockets.emit("data",{data:"This is handle by every client"})
app1.saveSession(client.id)
client.on("disconnect",function(){
app1.deleteSession(client.id)
console.log("client disconnected",client.id);
})
})
//And this is handle by particular client
var socketId=req.query.id
if(io.sockets.connected[socketId]!=null) {
io.sockets.connected[socketId].emit('particular User', {data: "Event response by particular user "});
}
And on client, it is very easy to handle.
var socket=io.connect("http://localhost:8080/")
socket.on("messages",function(data){
console.log("message is ",data);
//alert(data)
})
socket.on("data",function(data){
console.log("data is ",data);
//alert(data)
})
socket.on("particular User",function(data){
console.log("data from server ",data);
//alert(data)
})
As of version 1.4.5, be sure you provide a properly prefixed socketId in io.to().
I was taking the socketId the Client logged to debug and it was without prefix so I ended up searching forever till I found out! So you might have to do it like this if the Id you have is not prefixed:
io.to('/#' + socketId).emit('myevent', {foo: 'bar'});
io.sockets.sockets[socket.id].emit(...) worked for me in v0.9
Also you can keep clients refferences. But this makes your memmory busy.
Create an empty object and set your clients into it.
const myClientList = {};
server.on("connection", (socket) => {
console.info(`Client connected [id=${socket.id}]`);
myClientList[socket.id] = socket;
});
socket.on("disconnect", (socket) => {
delete myClientList[socket.id];
});
then call your specific client by id from the object
myClientList[specificId].emit("blabla","somedata");
Socket.IO allows you to “namespace” your sockets, which essentially means assigning different endpoints or paths.
This might help:
http://socket.io/docs/rooms-and-namespaces/