I am new to socket.io, and trying to figure out how to send multiple messages. Here is the scenario I am working on,
function setupServer(server) {
var socketIO = require("socket.io").listen(server);
socketIO.sockets.on('connection', function (socket) {
console.log("client is connected");
socket.emit('update', { progress: "starting..." })
});
}
I have to call setupServer(server) from another method, and I am receiving "starting..." on client side.
But the problem is, i want to send more/multiple messages LATER ON. Can not send array of messages as my application is building messages strings in say every 10 milliseconds (in a callback function) and i want to send as soon as they are created.
Any solution? Is it possible to get the socket object reference to reuse outside this function?
From socket.io docs: http://socket.io/docs/#broadcasting-messages
To broadcast, simply add a broadcast flag to emit and send method calls. Broadcasting means sending a message to everyone else except for the socket that starts it.
function setupServer(server) {
var socketIO = require("socket.io").listen(server);
socketIO.on('connection', function (socket) {
console.log("client is connected");
socket.emit('update', { progress: "starting..." });
startBroadCastFromSocket(socket, 3);
socket.on('disconnect', function(){
stopBroadcastFromSocket(socket);
});
});
// broadcast message to all sockets
setInteval(function() {
broadcastMessageToEveryone(socketIO, {body: 'Hello everyone (FROM SERVER)', timestamp: new Date());
}, 1000);
}
function broadcastMessageToEveryone(io, body) {
io.emit('message', {body: body, timestamp: new Date()});
}
function broadcastMessageFromSocket(socket, body) {
socket.broadcast.emit('message', {body: body, timestamp: new Date()});
}
var socketIntervals = {};
function stopBroadcastFromSocket(socket) {
if(socketIntervals[socket.id]) {
clearInterval(socketIntervals[socket.id]);
}
}
function startBroadcastFromSocket(socket, seconds) {
socketIntervals[socket.id] = setInterval(function(){
broadcastMessageFromSocket(socket, 'Hello!');
}, seconds*1000);
}
Related
Basically we use the socket to call deliverers for an app made in flutter, in the part of the app we made the socket work in the foreground service, however with the loss of internet connection the socket accumulates the emits and ends up crashing the app causing a memory leak.
I'm out of ideas to fix this now.
In the server side
global.ioDeliveryman = require('./socketio.js').init(deliveryManServer, {
pingInterval: 10,
pingTimeout: 5,
});
global.ioDeliveryman.on('connect', function (socket) {
socket.on('USER_CONNECTED', function(data){
global.connectedDeliverymanUsers[data.id] = {socket_id: socket.conn.id , aceite: data.aceite}
});
socket.on('USER_DISCONNECTED', function(data){
if(global.connectedDeliverymanUsers[data.id]){
try {
global.ioDeliveryman.sockets.connected[global.connectedDeliverymanUsers[data.id].socket_id].disconnect();
delete global.connectedDeliverymanUsers[data.id]
} catch (TypeError) {
console.log("Disconnect of undefined");
}
}
});
});
Client side
if (!socket.connected) {
conectar();
var jsao;
socket.on(describeEnum(SocketFunction.ALOCAR_ENTREGADOR), (data) async => {
socket.emit(describeEnum(SocketFunction.PONG), { "id": userId } ),
Vibration.vibrate(pattern: [1, 1000, 500, 2000]),
//the var jsao is setted here
NotificationUtils.showNotificationNovaCorrida(jsao),
await FGS.ForegroundService.sendToPort(jsao),
// check if app it's in the background aind wait 45 seconds to
Timer(Duration(seconds: 43), () async {
await FGS.ForegroundService.sendToPort('{"order": {"funcao": "VERIFICAR_BACKGROUND"}}');
if (isBackground) {
Timer(Duration(seconds: 2), () {
enviarAceiteEntregador(false, "${data["order"]["user_id"]}");
});
}
isBackground = true;
}),
});
Emit inside a function callback on backend
here we do ping pong to see if the user is connected, but this memory leak sometimes happens
it`s done in a loop with callback to cycle through the available deliverers one by one until one of them accepts
// PING
try{
if (alertMap.get(entregador.deliveryman_id.toString())) {
callback();
return;
}
//emit for deliveryman with socketId
global.ioDeliveryman.to(socketId).emit('ALOCAR_ENTREGADOR', {order: actualOrder, clientName: clientName});
} catch (TypeError) { console.log("Disconnect of undefined"); }
// PONG
pongMap.set(entregador.deliveryman_id.toString(), false);
everything works until the moment when the delivery person loses connection, then the socket starts to emit madly
I am new with the node js.
I am using node js with express.
I am create connection on server side via below code.
io.sockets.on('connection', function (socket) {
// console.log('A new socket connected with id : '+socket.id);
socket.on('error',function(e){
// console.log(e);
})
socket.on('disconnect',function(e){
// console.log( " \n disconnect \n ",e);
})
socket.on('UserRoom', function(data){
var user_id = data.user_id;
if(socket.adapter.rooms[user_id]===undefined)
{
console.log('Hey i am connected to server for User id => '+user_id);
socket.join(user_id);
}
else
{
console.log('Hey i am already connected to User id');
}
});
socket.on('JoinDraft', function(data)
{
var game_unique_id = data.game_unique_id;
socket.join(game_unique_id);
});
});
app.post('/game_update', function(req, res)
{
var target = true;
var response = '';
req.on('data', function (data) {
response += data;
});
req.on('end', function () {
res.sendStatus(200);
var result = JSON.parse(response);
game_update(result);
});
});
function game_update( result )
{
var game_unique_id = result ;
io.to(game_unique_id).emit('game_update', {"game_unique_id": game_unique_id});
};
client side code :- for joining room
function joinDraft_socket() {
// console.log(gameObj);
socket.emit('JoinDraft',{"game_unique_id" : gameObj.game_unique_id});
}
for getting node response , we have
socket.on('game_update', function(data) {
if(data.game_unique_id == gameObj.game_unique_id) {
console.log('Trigger to update ', data);
isYourPick();
}
});
Server node emit data single time to any room ( game_unique_id) then clients are receiving server ping multiple times.
Please let me know if any one face this kind of issue and how they resolved it.
Below is image of console after single update of server node , client receive multiple hits
Any help is appreciate ...
Thanks in Advance
Not sure if the issue is how I have my sockets setup - or if I am incorrectly trying to render the data with React.
I can successfully pull in data with my socket - yet it doesn't live update state when new data is posted to the server. My intention is for the state in React to automatically render new data, which is always live because of the socket connection.
Here is my client app that gets messages from the server and renders them:
var Chat = React.createClass({
getInitialState: function() {
return {
messages: null
}
},
componentWillMount: function(){
var self = this;
socket.emit('getMessages');
socket.on('serverMessages', function (data) {
self.setState({messages: data})
});
},
render: function() {
var messages = this.state.messages ? <MessageList messages={this.state.messages}/> : null
return (
<div className="jumbotron">
{ messages }
<MessageForm submitMessage={this.submitMessage}/>
</div>
);
}
});
Just in case here is my server code that emits data:
io.on('connection', function (socket) {
socket.on('getMessages', function (data) {
Message.find(function(err, messages){
socket.emit('serverMessages', messages);
})
});
});
As of right now, you're "just" grabbing data from the server once the component has been loaded. To get something a bit more "real time" you'll want to either ping the server with the same emit statement you specified regularly (which defeats the point of using websockets, really, you could use long-polling) or have the server regularly send new data to all clients.
You can do EITHER:
A) Client side: "Polling" for information [Not Ideal]
Note: I initially put this in my answer because I saw the OP was "polling" when the controller was loaded. I didn't click on that this might be because the controller may not be loaded with the websocket so sending data on connect might not work here. My bad.
Replace socket.emit('getMessages') with something that will "poll" the websocket regularly for data:
setInterval(function () {
socket.emit('getMessages')
}, 10000); /* Request data from the socket every 10 seconds */
OR
B) Server side: Send new data as it becomes available. [Best Way]
Track all clients via a clients array and delete them from it when their session ends.
var clients = [];
io.on('connection', function (socket) {
clients.push(socket);
socket.on('end', function () {
// Could also splice the array below, but it still works.
delete clients[clients.indexOf(socket)];
});
/* Previous logic for server goes here */
});
Run this code when you need to push new messages from the database/data storage:
for (var i in clients) {
clients[i].emit('serverMessages', /* messages object */);
}
Your server code is only firing upon initial socket connection.
Server:
socket.on('getMessages', function (data) {
Message.find(function(err, messages){
socket.emit('serverMessages', messages);
})
});
Client:
var Chat = React.createClass({
getInitialState: function() {
return {
messages: null
}
},
componentWillMount: function(){
var self = this;
socket.emit('getMessages');
socket.on('serverMessages', function (data) {
self.setState({messages: data})
});
},
render: function() {
var messages = this.state.messages ? <MessageList messages={this.state.messages}/> : null
return (
<div className="jumbotron">
{ messages }
</div>
);
}
});
Based on naming convention, it also appears that your Message.find() is pulling a single message. I would recommend clarifying the labeling to match cardinality.
Try this:
var Chat = React.createClass({
getInitialState: function() {
return {
messages: null
}
},
componentWillMount: function(){
var self = this;
socket.emit('getMessages');
socket.on('serverMessages', function (data) {
self.setState({messages: data})
});
},
render: function() {
var messages = this.state.messages ? <MessageList messages={this.state.messages}/> : null
return (
<div className="jumbotron">
{ messages }
<MessageForm submitMessage={this.submitMessage}/>
</div>
);
}
});
Could it be possible its due to the componentWillMount lifecycle method? Could you try the componentDidMount instead.
It looks like render will see the state update but only gets executed once despite the state change according to facebook.
I'm trying to learn Socket.io by building a set of dynamically created chatrooms that emit 'connected' and 'disconnected' messages when users enter and leave. After looking at a couple of questions I've put together something functional but most of the response linked are from people who admit they've hacked together answers and I've noticed there's a more general - and recent - discussion about the right way to do this on the Socket.io repo (notably here and here)
As I'm such a novice I don't know if the work below is an acceptable way to do things or it just happens to incidentally function but will cause performance issues or result in too many listeners. If there's an ideal - and official - way to join and leave rooms that feels less clunky than this I'd love to learn about it.
Client
var roomId = ChatRoomData._id // comes from a factory
function init() {
// Make sure the Socket is connected
if (!Socket.socket) {
Socket.connect();
}
// Sends roomId to server
Socket.on('connect', function() {
Socket.emit('room', roomId);
});
// Remove the event listener when the controller instance is destroyed
$scope.$on('$destroy', function () {
Socket.removeListener('connect');
});
}
init();
Server
io.sockets.once('connection', function(socket){
socket.on('room', function(room){ // take room variable from client side
socket.join(room) // and join it
io.sockets.in(room).emit('message', { // Emits a status message to the connect room when a socket client is connected
type: 'status',
text: 'Is now connected',
created: Date.now(),
username: socket.request.user.username
});
socket.on('disconnect', function () { // Emits a status message to the connected room when a socket client is disconnected
io.sockets.in(room).emit({
type: 'status',
text: 'disconnected',
created: Date.now(),
username: socket.request.user.username
});
})
});
Socket.IO : recently released v2.0.3
Regarding joining / leaving rooms [read the docs.]
To join a room is as simple as socket.join('roomName')
//:JOIN:Client Supplied Room
socket.on('subscribe',function(room){
try{
console.log('[socket]','join room :',room)
socket.join(room);
socket.to(room).emit('user joined', socket.id);
}catch(e){
console.log('[error]','join room :',e);
socket.emit('error','couldnt perform requested action');
}
})
and to leave a room, simple as socket.leave('roomName'); :
//:LEAVE:Client Supplied Room
socket.on('unsubscribe',function(room){
try{
console.log('[socket]','leave room :', room);
socket.leave(room);
socket.to(room).emit('user left', socket.id);
}catch(e){
console.log('[error]','leave room :', e);
socket.emit('error','couldnt perform requested action');
}
})
Informing the room that a room user is disconnecting
Not able to get the list of rooms the client is currently in on disconnect event
Has been fixed (Add a 'disconnecting' event to access to socket.rooms upon disconnection)
socket.on('disconnect', function(){(
/*
socket.rooms is empty here
leaveAll() has already been called
*/
});
socket.on('disconnecting', function(){
// socket.rooms should isn't empty here
var rooms = socket.rooms.slice();
/*
here you can iterate over the rooms and emit to each
of those rooms where the disconnecting user was.
*/
});
Now to send to a specific room :
// sending to all clients in 'roomName' room except sender
socket.to('roomName').emit('event', 'content');
Socket.IO Emit Cheatsheet
This is how I inform users of a "disconnecting user"
socket.on('disconnecting', function(){
console.log("disconnecting.. ", socket.id)
notifyFriendOfDisconnect(socket)
});
function notifyFriendOfDisconnect(socket){
var rooms = Object.keys(socket.rooms);
rooms.forEach(function(room){
socket.to(room).emit('connection left', socket.id + ' has left');
});
}
Here is a working method.
I am using socketio "socket.io": "^2.3.0" on server side. On Client side is android
// SocketIO
implementation('io.socket:socket.io-client:1.0.0') {
// excluding org.json which is provided by Android
exclude group: 'org.json', module: 'json'
}
Following is the code that is working for me.
// Join Chat Room
socket.on('ic_join', function(data) {
// Json Parse String To Access Child Elements
let messageJson = JSON.parse(data)
let room1 = messageJson.room1
console.log('======Joined Room========== ')
console.log(room1)
socket.join(room1, function(err) {
console.log(io.sockets.adapter.rooms[room1].length);
console.log(err)
})
})
// Leave Chat Room
socket.on('ic_leave', function(data) {
// Json Parse String To Access Child Elements
let messageJson = JSON.parse(data)
let room1 = messageJson.room1
console.log('======Left Room========== ')
console.log(room1)
socket.leave(room1, function(err) {
if (typeof io.sockets.adapter.rooms[room1] !== 'undefined' && io.sockets.adapter.rooms[room1] != null) {
console.log(io.sockets.adapter.rooms[room1].length);
console.log(err)
} else{
console.log("room is deleted")
}
})
})
For anyone reading this beyond 2/1/2021, using socket.io 3.1.0 and hopefully later, you can reference my example. I have found that the example on how to do this in socket.io's documentation is incorrect. They claim that in the disconnect event that socket.rooms is an object. While is uses the block container and is comma separated, there are no key pairs, meaning their demonstration of const rooms = Object.keys(socket.rooms) returns an empty value. It's creating an array out of an object that is really just an array. To my knowledge {} can only be used for block statements and objects. By some quirk, NodeJS is treating it like a normal array. I assign custom, 4 digit rooms on each connect event. So I have on the disconnect event I have the server skim through all the rooms, and if it encounters a room name with a length of 4, it tells everyone in the room that the size of the room decreased by one. On the client side, I have a socket event listener monitoring for this and when it's detected, updates a innerHTML property so that the clients can display the number of connected users.
//client side code:
socket.on('roomSize', (roomSize) => {
document.getElementById('clientCount').innerHTML = roomSize + ' clients connected';
});
//Server side code:
io.on("connection", (socket) => {
socket.on('createRoom', () => {
let ID = makeID(4)
while (io.sockets.adapter.rooms.has(ID)) {
ID = makeID(4)
}
socket.join(ID);
socket.emit('setID', ID);
});
socket.on("joinRoom", (room) => {
socket.join(room);
let roomSize = io.sockets.adapter.rooms.get(room).size
io.in(room).emit('roomSize', roomSize);
socket.emit('roomJoined', room, roomSize);
});
socket.on('disconnecting', function() {
let rooms = socket.rooms;
rooms.forEach(function(room) {
if (room.length === 4) {
let roomSize = io.sockets.adapter.rooms.get(room).size - 1
io.in(room).emit('roomSize', roomSize);
}
});
});
function makeID(length) {
var result = '';
var characters = '0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
})
With node.js, I'm trying to send the current server_time to all clients in every second.
Therefore, I wanted to use setInterval() to emit an event to all clients and sending the time, but it doesn't work. Did I define the setInterval function at the right place or did missed something else?
var http = require("http");
var socketIO = require('socket.io');
var connect = require('connect');
//keep track of every connected client
var clients = {};
//create Server
var httpServer = connect.createServer(
connect.static(__dirname)
).listen(8888);
//socket
var io = socketIO.listen(httpServer);
io.sockets.on('connection', function (socket) {
//add current client id to array
clients[socket.id] = socket;
socket.on('close', function() {
delete clients[socket.fd]; // remove the client.
});
//send news on connection to client
socket.emit('news', { hello: 'world' });
//this one works fine!
//send server time on connection to client
socket.emit("server_time", { time: new Date().toString() });
});
//this doesn't work!
// Write the time to all clients every second.
setInterval(function() {
var i, sock;
for (i in clients) {
sock = clients[i];
if (sock.writable) { // in case it closed while we are iterating.
sock.emit("server_time", {
console.log("server_time sended");
time: new Date().toString()
});
}
}
}, 1000); //every second
May I suggest a workaround/improvement that should fix the problem. Add the clients to a chat room. Somewhere in:
io.sockets.on('connection', function (socket) {
add a
socket.join('timer');
Then the setIntervall would be
setInterval(function() {
io.sockets.in('timer').emit("server_time", { time: new Date().toString() })
}, 1000);
Hope this works for you!
The problem is the following function:
if (sock.writable) { // in case it closed while we are iterating.
sock.emit("server_time", {
// console.log("server_time sended"); // get rid of this line -> invalid code
time: new Date().toString()
});
}
sock.writable is undefined and therefore the emit event is never sent. Set the property to true on connection and to false on close.