I am creating a real-time app using. Socket IO, Node.js, Express.js, React for frontend, and Microsoft SQL for database. I only want to send data when the database is updated or when a new client is connected. Though when the client first connects, the IO connection fires off sending my data to the new client. But when I make a change to my database. The data never gets sent. My code is below. I feel as though I am close, but I am just missing something that makes the code work. I appreciate any kind of help.
const app = express();
const httpServer = require('http').createServer(app);
const io = require('socket.io')(httpServer);
const path = __dirname + '/views/';
let sqlQuery = require('./controllers/sqlController').queryDatabase;
let currentQueryData = {};
let connectedSocketID
let objectMatched = true;
app.use(express.static(path))
app.get('/', function (req,res) {
res.sendFile(path + "index.html");
});
// Function to emit data only when a change is found.
const sendData = (data, socket) => {
socket.emit('markerCreation', data);
}
// compare both objects and return a boolean value.
const compareObjects = (object1, object2) => {
return JSON.stringify(object1) === JSON.stringify(object2);
}
httpServer.listen(3001, () => {
console.log(`Server listening at ${3001}`);
})
io.on('connection', async socket => {
// Get new Query Data than compare the object with the currently saved Query data
let newQueryData = await sqlQuery();
objectMatched = compareObjects(currentQueryData, newQueryData)
if(!objectMatched) { // If objects matched is not true take the new data and save it in currentQueryData and send data to client.
currentQueryData = newQueryData;
sendData(currentQueryData, socket);
} else if (connectedSocketID !== socket.id) { // If socket is not already connected saved it in connected sockets and send data to client
connectedSocketID = socket.id;
sendData(currentQueryData, socket);
};
// Issue: Socket IO will stop sending to connected Client. If a new update happens on the sql database the change isn't passed along to
// the client.
});```
Related
I am getting confused with this Node.js, Angular 13 and Socket IO scenario.
First of all let's asume we are already saving all required info in a Database, like roomId, roomOwner, username etc.
So, let's say we want to create an Online Quizz game using sockets to sync all players, 6 max for this scenario. HOWEVER, this is the problem...
On the Angular code there is this Service which is connecting Client
with Back-End
SocketService.ts
export class SocketService {
socket: any;
readonly url: string = "ws://localhost:3000";
constructor() {
this.socket = io(this.url)
}
}
On the Server side index.js inits webSocket
index.js
const app = express();
const io = require('./sockets/websocket')(app);
Inside webSocket.js we create the instance of socketIO to be exported and used across the whole back-end controllers as needed
webSocket.js
module.exports = function(app){
this.server = require('http').createServer(app);
this.socket = require('socket.io');
this.io = socket(server, {
cors: {
origin: "https://localhost:4200",
credentials: true
}
});
this.server.listen(3000, () => {
console.log("Socket IO is lestineng on port 3000");
});
io.on("connection", function (socket) {
console.log("A user connected");
});
this.registerSocketToRoom = function(roomId){
try{
console.log('[socket]','join room :',roomId)
io.join(roomId);
io.sockets.to(roomId).emit('user joined', socket.id);
}catch(e){
console.log('[error]','join room :',e);
io.emit('error','couldnt perform requested action');
}
} }
This is an example controller. We import the exported instance of SocketIO exported from webSocket.js file. Let's say we want to join a room if Client makes an http request to join a room HOWEVER, WE DID NOT joined the room "on socket connection" so we have to do it now. We try to use the exported method {registerSocketToRoom}.
GameRoomManagerController.js
require('../../sockets/websocket');
... // Some code here
exports.joinGameRoom = function(req, res){
const roomId = req.params.roomId;
console.log(roomId);
registerSocketToRoom(roomId);
return res.send({status: "success", msg: `joined Room: ${roomId}` });
}
When executing the process of creating a room -> saving the info to the DB -> Join Room the following error occurs.
TypeError: io.sockets.join is not a function
In theory this sound right to me, but I think I am misunderstanding the difference between io and socket.
Can someone explain to me what's going on here? Is it even possible
to export the same instance of io to be used in any place of the
back-end?
Is it even possible to join a room AFTER the connection was
created?
What's the difference between io and socket?
Before starting the topic, it is better to get acquainted with some terms from the socket.io library
io
In fact, it refers to all sockets connected to the server. You can
send messages individually, in groups, or to all sockets.
Your idea of the socket that is written in this way
io.on('connection', socket => {
socket.on('message', data => {
});
});
In this section, you can only read the information related to this
event or you can transfer this information between sockets
Well, now we are going to solve this problem. The reason for this error is not following this hierarchy in your coding. I suggest you refer to the socket.io document next time and strengthen your foundation.
And finally, I will provide you with a simple example of the correct implementation method
let app = require('express')(),
http = require('http').Server(app),
io = require('socket.io')(http);
let listOfRoom = [];
io.on('connection', socket => {
let joinUserInRoom = (roomId) => {
if (socket.adapter.rooms.has(roomId) === false) {
listOfRoom.push(roomId);
socket.join(roomId);
}
},
leaveUserInRoom = (roomId) => {
if (listOfRoom.includes(roomId)) {
listOfRoom.splice(listOfRoom.indexOf(roomId), 1);
socket.leave(roomId);
}
};
socket.on('joinRoom', data => {
joinUserInRoom(data.roomId);
})
socket.on('disconnect', data => {
leaveUserInRoom(data.roomId);
});
socket.on('messageRoom', data => {
io.to(data.roomId).emit('eventMessageRoom', data); // send data in special room
});
});
Is it possible to adapt a json from a live api according to the changes in the database?
server.js
const connection = mongoose.connection;
connection.once("open", () => {
//Live Stream - Posts
const observePosr_changes = connection.collection("posts").watch();
//Observe change in Data Base
observePosr_changes.on("change", (change) => {
//console.log('changes right now ->',change);
switch (change.operationType) {
//create request
case "insert":
//Create posts -> operationType function
break;
//patch/put request
case "update":
//Update posts -> operationType function
break;
//delete request
case "delete":
//Update posts -> operationType function
break;
}
});
});
I found using the documentation from mongodb a method by which I can detect live the changes in db atnci when post / patch / delete
controller/postController.js
//Create a new post - tahe all value and add into DB
exports.createPost = catchAsync(async(req,res)=>{
const create = await Post.create(req.body);
res.status(201).json({
status:"success",
data:create
});
});
//Get Information from DB
exports.getAllPosts = catchAsync(async(req,res,next)=>{
const getAll = await Post.find()
res.status(200).json({
status:"success",
data:{
post:getAll
}
});
});
Is there a possibility to use the socket in this situation to make the application live.
That is, at the moment the mobile application and the website to see the newly added content must refresh.
you want to configure the server first
io = socket(server); -- server : express or any other
io.on("connection", function (socket) {
//console.log("Made socket connection");
});
so you can connect the socket from your client app using unqiue event name
this.socket = io.connect(YOUR_URL);
this.socket.on(HERE_YOUR_EVENT_NAME, (data: any) => {
-- your get the data here
});
when ever you want to send the data to client app emit the data using event name in server side using below code
io.emit(event_name, data);
I'm creating an app that allows users to have conversations with other individual users.
As a user in the app, whenever someone I have a conversation with, is connecting to the app, I would like to get a message alerting me he is online.
For that purpose, I'm using node.js with socketIO and react.
The way I implemented the following in my server is:
const socketio = require('socket.io');
const io = socketio(server);
io.on('connection', async (socket) => {
const { user } = socket.request;
const userConversationIds = user.conversations;
socket.join(conversationIds);
let ioToConversations = io;
userConversationIds.forEach((conversationId) => {
ioToConversations = ioToConversations.to(conversationId);
});
ioToConversations.emit('online', `${user.firstName} ${user.lastName} is now online!`);
});
and on the client-side:
import io from 'socket.io-client';
const socket = io();
componentDidMount () => {
socket.on('online', (messageText) => {
console.log(messageText); // eslint-disable-line
});
};
All of the above resulted in a "user in now online" message, only to the now-connected-user itself.
I would want the other users in the conversation to have that message, and the connected user itself to have none.
What am I doing wrong here?
One mistake could be that you didn't connect to the server properly, you need to connect using this code and replacing <port> with your port number assuming that you are running locally:
io.connect("http://localhost:<port>")
Another mistake could be that you didn't used the method .io() and .emit() at the same time. What i mean by that is that you used the .emit() method after the forEach loop. You could try doing this instead:
io.on("connection", async (socket) => {
const { user } = socket.request;
const userConversationIds = user.conversations;
let ioToConversations = io;
userConversationIds.forEach((conversationId) => {
ioToConversations = ioToConversations
.to(conversationId)
.emit("online", `${user.firstName} ${user.lastName} is now online!`);
});
});
Another error could be because you didn't make the socket of each user join the specific rooms with the .join() method. You could do it with the following:
userConversationIds.forEach((conversationId) => {
socket.join(conversationId);
});
This code should be placed before the forEach loop that emits the message.
You could also join and emit at the same, however i have not tested with the following:
userConversationIds.forEach((conversationId) => {
socket.join(conversationId);
ioToConversations = ioToConversations
.to(conversationId)
.emit("online", `${user.firstName} ${user.lastName} is now online!`);
});
I am using Socket.io 2.0.4 and React.js (CRA)
Background: In my server code (server.js) once someone sucessfully joins a room I want to emit an event that tells all clients in that room that someone joined.
Problem: I don't get an error but nothing get's transmitted to my client if I try to use .to(room) or .in(room) in conjuction with .emit... but .emit will work on it's own.
What Works: I am successfully able to implement to the socket.join() code and in the callback I console.log the IDs of each person that's joined using the showClients function I created. I can see each person join one at a time via console.
Notes: I store the room name in the data variable and access it using data.room but I've also just wrote in the room name manually to no avail.
Client Code (abridged)
import React, {Component} from 'react';
import Card from '../card/card';
import './game.css';
const io = require('socket.io-client');
const socket = io()
class Game extends Component {
constructor(props){
super();
}
componentDidMount(){
this.gameCode();
this.cardSelected();
socket.on("cardDiscarded", this.updateDiscard);
socket.on("playerJoined", () => {
alert("hi!");
console.log("YAY!!!!!!!!!!!!!!!!!!!!");
});
}
//....rest of my code....
}
Server Code (abridged)
Look at the joinRoom function to see the issue
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
const http = require("http").Server(app);
var io = require("socket.io")(http);
var bodyParser = require('body-parser');
console.log("Hello world!");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
io.on("connection", (socket) => {
socket.on("createNewRoom", (name) =>{
CreateNewRoom();
console.log("NEW ROOM CREATED!");
})
socket.on("joinRoomRequest", (name, room) =>{
// console.log(name, room);
console.log("Request to join room");
var data = {
name: name,
room: room
};
joinRoom(data);
})
function CreateNewRoom() {
//Get unique room number
var thisGameId = (Math.random() * 1000000 ) | 0;
//Send the room number to the browser
socket.emit('newRoomCreated', {roomID: thisGameId, mySocketID: socket.id});
//Tell socket.io this user is joining this room
socket.join(thisGameId.toString());
console.log(thisGameId);
};
function joinRoom(data){
console.log("trying to join room:" + data.room);
data.socketID = socket.id;
console.log(data);
socket.join(data.room, () => {
let rooms = Object.keys(socket.rooms);
//Let the clients know a player has joined
console.log(rooms);
console.log(data.name + " JOINED room " + data.room);
showClients(data.room);
io.to(data.room).emit("playerJoined"); //<----- DOESN't WORK
});
}
function showClients(room){
var roomClients = io.of('/').in(room).clients((err, data)=>{
if (err) throw err;
console.log("The people in room ", room, " are: ", data);
})
}
})
I managed to solve it like this:
does not work:
socket.join (my_room)
work:
socket.join (my_room.toString ())
I believe io.in(room).emit(data) is what you are looking for. I recently ran into this problem as well. According to the documentation, if you want to emit to everyone accept 'socket' (aka the user who joined) you use socket.to(room).emit. If you want to emit to everyone including the user, you use io.in(room).emit
I am using socket.io for private chatting for the server side I am using
socket.broadcast.to(receiver_socket_id).emit('message', data); // where data is a json object containing text
And at the client side code I catch the data using
socket.on('message', function (data) {
alert(data. text);
});
Its working properly and showing the alert on that specific user (socket id) ‘s panel when only two socket are connected (sender and receiver). But the problem appears when one more user connects to that socket then I see two alerts and when total 4 user connected (sender + receiver + two others) then see 3 alerts. But the good point is I can see the alerts only that specific client's panel not the others.
I can’t understand the problem, please help.
Please have a look on it
gyazo.com/a98d3a64a9fc6487e6ded8ccd90fd5ab
it prints test three times because three browsers are opened.
Full code here:
Sever side (I have used Redis):
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('redis');
server.listen(8080);
var usernames = {};
io.on('connection', function (socket) {
console.log(socket.id);
socket.on('adduser', function (userId) {
usernames[userId] = socket.id;
});
var redisClient = redis.createClient();
redisClient.subscribe('message-channel');
redisClient.on('message', function (channel, data) {
var jsonObj = JSON.parse(data);
var rcvrId = jsonObj.rcvrId;
socket.broadcast.to(usernames[rcvrId]).emit('message', data); // Not throwing error....should work
});
socket.on('disconnect', function () {
console.log(socket.id + ' Disconnected');
redisClient.quit();
});
});
Client side:
var socket = io.connect('http://localhost:8080');
var userId = $('input[name="userId"]').val();
var rcvrId = $('input[name="rcvrId"]').val();
socket.on('connect', function () {
// call the server-side function 'adduser' and send one parameter (value of prompt)
socket.emit('adduser', userId);
});
socket.on('message', function (data) {
data = jQuery.parseJSON(data);
console.log(data);
$("#messages").append("<div><strong>" + data.userId + " : </strong><span>" + data.message + "</span></div>");
});
You can use io.of('/').sockets[rcvrId].emit('message', data). In case you are using a different namespace just replace the / with your namespace.