I am trying to build socket connections between React client-side and Node.js server-side. But the server will host two sockets. Here is the server-side code
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(2893, {
path: "/ws",
resource: "/ws",
transports: ['websocket'],
pingTimeout: 5000
});
var redis = require('redis');
const subscriber = redis.createClient();
require('./server/route')(app, io);
require('./server/lib/subscriber')(require('socket.io').listen(server), subscriber);
The first socket connection is ok, but I wonder why the second one is not working (which is attached with listen(server). Here is subscriber module I wrote:
module.exports = (io, subscriber) => {
io.sockets.on('connection', (socket) => {
console.log(socket);
socket.on('room', (room) => {
socket.join(room);
});
});
subscriber.on('pmessage', (pattern, channel, message) => {
const msg = JSON.parse(message);
const idCallcenter = msg.idCallcenter;
return io.to(idCallcenter).emit('message', { type: channel, message: msg });
});
subscriber.psubscribe('*');
};
And the client-side React module
var socketOption = { path: "/ws", transports: ['websocket'] };
var socket = io("http://localhost:2893", socketOption);
var socket2 = io.connect("http://localhost:4004");
export default function (user) {
debugger
socket.user = user;
contact(socket);
notify(socket);
socket.on('connect', function () {
debug('socket connect', socket.id);
store.dispatch(connectNetworkSuccess());
socket.emit('user-online', {
idUser: user._id,
idCallcenter: user.idCallcenter,
email: user.email
});
});
socket2.on('connect', () => {
debug('Socket connected');
socket2.emit('room', user.idCallcenter);
});
socket2.on('message', (data) => {
debugger
debug('Socket message');
debug(data);
const type = data.type;
const message = data.message;
if (type === 'recordFetched') {
}
});
socket.emit('user-online', {
idUser: user._id,
idCallcenter: user.idCallcenter,
email: user.email
});
socket.on('disconnect', function (reason) {
debug('socket disconnect', reason);
store.dispatch(connectNetworkFailed());
});
}
The first socket (in port 2893) runs normally. Meanwhile, socket2 (in port 4004) does not connect. It does not jump into connection callback of both server and client sides. What did I do wrong here?
I solved the case myself. The working code on client side is:
export default function (user) {
debugger
var socketOption = { path: "/ws", transports: ['websocket'] };
var socket = env === "local" ? io("http://localhost:2893", socketOption) : io(window.location.origin, socketOption);
var socket2 = io.connect();
socket.user = user;
contact(socket);
notify(socket);
socket.on('connect', function () {
debug('socket connect', socket.id);
store.dispatch(connectNetworkSuccess());
socket.emit('user-online', {
idUser: user._id,
idCallcenter: user.idCallcenter,
email: user.email
});
});
socket2.on('connect', () => {
console.log('Socket connected');
socket2.emit('room', user.idCallcenter);
});
socket2.on('message', (data) => {
debugger
console.log('Socket message', data);
const type = data.type;
const message = data.message;
if (type === 'recordFetched') {
}
});
socket.emit('user-online', {
idUser: user._id,
idCallcenter: user.idCallcenter,
email: user.email
});
socket.on('disconnect', function (reason) {
debug('socket disconnect', reason);
store.dispatch(connectNetworkFailed());
});
}
The server did jump into connection callback, but not room callback. I suppose it is because the connect callback of client side was defined after the connection is made, so that it couldn't jump into it. This is my possibility. Am I right?
Related
I am using the following logic to verify whether client is connected to websocket or not: Client keeps sending pings at an interval to the server. Server has a timeout which triggers an event on completing. As soon as Client pings server, the server timeout resets. This works fine when one client is connected. But the logic breaks on multiple clients. How can I fix this? As soon as 2nd client connects, Server says client 1 has disconnected and doesn't print anything when either of the 2 disconnect.
This is my server logic:
const WebSocket = require("ws");
const express = require("express");
const app = express();
const server = require("http").createServer(app);
const url = require('url');
const PORT = process.env.PORT || 3000;
const wss = new WebSocket.Server({ server: server });
app.get("/", (req, res) => res.send("Temst."));
var tm;
function ping(client) {
tm = setTimeout(function () {
console.log(`[-] ${client} Disconnected`);
wss.emit("customClose", client);
}, 5000);
}
function pong(client) {
clearInterval(tm);
// console.log("[!] Cleared timeout");
ping(client);
}
wss.on("connection", function connection(ws, req) {
var queryData = url.parse(req.url,true).query;
ping(queryData.id);
console.log(`[+] ${req.socket.remoteAddress} Connected`);
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
message = {
type: "alert",
msg: `${queryData.id} has Connected.`,
};
client.send(JSON.stringify(message), { binary: false });
}
});
ws.on("message", function incoming(message) {
if (message == "__ping__") {
console.log(`[!] Ping Receieved from ${req.socket.remoteAddress}`);
pong(queryData.id);
} else {
`[!] Message Receieved from ${req.socket.remoteAddress}`;
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(msg, { binary: false });
}
});
}
});
});
wss.addListener("customClose", function (m) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
message = {
type: "alert",
msg: `${m} has Disconnected.`,
};
client.send(JSON.stringify(message), { binary: false });
}
});
});
server.listen(PORT, () => console.log("Listening on port 3000"));
I think I have solved the problem. After looking at the ws package docs, I tried server side pinging instead of client side. It is working for multiple users for now. Will update if any problems occur.
const WebSocket = require("ws");
const express = require("express");
const app = express();
const server = require("http").createServer(app);
const url = require("url");
const PORT = process.env.PORT || 3000;
const wss = new WebSocket.Server({ server: server });
app.get("/", (req, res) => res.send("Temst."));
var myClients = [];
wss.on("connection", function connection(ws, req) {
var queryData = url.parse(req.url, true).query;
myClients.push({
id: queryData.id,
wsoc: ws,
isAlive: true,
});
console.log(`[+] ${req.socket.remoteAddress} Connected`);
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
message = {
type: "alert",
msg: `${queryData.id} has Connected.`,
};
client.send(JSON.stringify(message), { binary: false });
}
});
ws.on("pong", () => {
let x = myClients.find((o) => o.wsoc === ws);
x.isAlive = true;
});
ws.on("message", function incoming(message) {
console.log(`[!] Message Receieved from ${req.socket.remoteAddress}`);
msg = JSON.parse(message);
console.log(queryData);
msg = { ...msg, time: new Date().toISOString() };
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(msg), { binary: false });
}
});
});
});
wss.addListener("customClose", function (m) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
message = {
type: "alert",
msg: `${m} has Disconnected.`,
};
client.send(JSON.stringify(message), { binary: false });
}
});
});
const interval = setInterval(function ping() {
myClients.forEach((clnt, index) => {
if (clnt.isAlive === false) {
console.log("[-]", clnt.id, "has Disconnected.");
wss.emit("customClose", clnt.id);
clnt.wsoc.terminate();
myClients.splice(index, 1);
}
clnt.isAlive = false;
clnt.wsoc.ping();
});
}, 5000);
server.listen(PORT, () => console.log("Listening on port 3000"));
I try to test socket.io using mocha, but i have a problem when make a socket call on. I think socket is not connected, because the console log not triggered but the console.log outside the socket call is show. Or it's wrong about how i use the done() method ?
const { assert } = require('chai');
const ioServer = require('socket.io');
const ioClient = require('socket.io-client');
const http = require('http');
const socketUrl = 'http://localhost:5000';
const options = {
transports: ['websocket'],
'force new connection': true,
};
let server;
let client;
let httpServer;
describe('Socket', () => {
beforeEach(done => {
httpServer = http.createServer().listen();
server = ioServer(httpServer);
client = ioClient.connect(socketUrl, options);
server.on('connection', socket => {
console.log('connected server');
socket.emit('echo', 'Hello World');
});
client.on('echo', message => {
console.log('connected client');
console.log(message);
});
console.log('test');
done();
});
afterEach(done => {
server.close();
client.close();
done();
});
it('It should connect socket', done => {
client.on('echo', message => {
assert.equal(message, 'Hello World');
});
done();
});
});
Here's the screenshoot cli terminal when running mocha.
Mocha
I'm running a Node Socket.io server with Express with docker compose
and client side is vue.js. The service is running well, but the CPU grows 100
and stays, i'm every time doing restart to my docker compose,still after 5 minutes cpu reaches jumping 100%.. I don't know what to check since there's laterally nothing but the most basic stuff..client and server connection and disconnect works perfect,all events is working good but i don't know which way i ll focus to find my bugs.
Here are my backend code socket code.
import { verifyToken } from './middleware/auth';
import Socket from './components/services/socket';
const app = express();
const socketPort = process.env.SOCKET_PORT || 8001;
const io = require('socket.io')(socketPort);
const port = process.env.PORT || 3000;
app.listen(port, () => {
log.info(`HTTP server is running 🚀 on port ${port}
Socket service running 🚀 on port ${socketPort}
Process Pid: ${process.pid}`);
});
const socket = new Socket(io);
socket.modelLoaded();
socket.js file______________
import { socketTokenVerification } from './../../middleware/auth';
export default class {
constructor(io) {
this.io = io;
this.connections = {};
}
addConnection(socket) {
if (this.connections[socket.user_id]) {
this.socketDisconnect(this.connections[socket.user_id]);
}
this.connections[socket.user_id] = {
id: socket.id,
isAuthenticated: true,
user_id: socket.user_id
};
}
removeConnection(socket) {
delete this.connections[socket.user_id];
}
socketDisconnect(socket) {
if (this.io.sockets.connected[socket.id]) {
this.io.sockets.connected[socket.id].disconnect(true);
}
}
modelLoaded() {
const { io, connections } = this;
io.on('connection', socket => {
const authData = socketTokenVerification(socket);
if (authData.error) {
io.to(socket.id).emit('error', {
message: authData.message
});
this.socketDisconnect(socket);
return;
}
const { user_id } = authData;
socket.user_id = user_id;
this.addConnection(socket);
User.connected(socket.user_id);
socket.on('disconnect', () => {
this.removeConnection(socket);
User.disconnect(user_id);
this.socketDisconnect(socket);
Object.keys(io.sockets.connected).length);
});
Client side code.
import io from 'socket.io-client';
socket = io(ENDPOINT, {
transports: ['websocket'],
});
socket.on('connect', () => {
console.log('conected');
});
socket.on('disconnect', () => {
console.log('disconnected');
});
socket.on('reconnect', () => {
console.log('reconnect');
});
socket.on('receive_message', (receiveMsg) => {
console.log(recMsg, 'mes');
});
socket.on('error', (err) => {
console.log(err, 'err');
});
},
beforeDestroy() {
socket.disconnect(true);
},
when users online and don't close our clients such as browser tab or android application, i can send message to each specific user by
socket.broadcast.to(socketId)
.emit('new message', {
username: data.fromUsername,
money : 'Hurrraaa'
});
when users close clients as mobile application this event don't trigger but i can send any message to broadcast as:
socket.broadcast.emit('new message', "hooooorrrrraaaaa");
my users don't use client application any time, but i need to send message to some specific user and notify user until opening application and see message, users should be on'time in my application to get every message which i want to send from server like with Chat messengers which don't need users currently are using application such as WhatsApp, how can i resolve this problem?
then problem is send message to some specific users when they are istalled application and logged ti sever, but not using now and application waiting to receive message such as broadcast or special message to himself
this code is my simplified server:
var socket = require('socket.io'),
express = require('express'),
app = express(),
server = require('http').createServer(app),
io = socket.listen(server),
port = process.env.PORT || 3000,
mysql = require('mysql'),
uuid = require('node-uuid'),
datetime = require('node-datetime'),
moment = require('moment'),
bcrypt = require('bcrypt'),
async = require('async'),
request = require('request'),
redis = require("redis"),
redisClient = redis.createClient(),
forever = require('forever'),
log = require('log4node');
var io_redis = require('socket.io-redis');
io.adapter(io_redis({host: 'localhost', port: 6379}));
require('sticky-socket-cluster/replace-console')();
var options = {
workers : require('os').cpus().length,
first_port : 8000,
proxy_port : 3000,
session_hash: function (req, res) {
return req.connection.remoteAddress;
},
no_sockets: false
};
require('sticky-socket-cluster')(options, start);
function start(port) {
io.sockets.on('connection', function (socket) {
socket.on('new message', function (data) {
socket.broadcast.emit('new message', "hooooorrrrraaaaa");
});
socket.on('login', function (data) {
log.info(JSON.stringify(data))
login(data.username, data.password, function (success, value) {
if (success) {
redisClient.exists(data.username, function (err, doesExist) {
if (err) return;
if (!doesExist) {
redisClient.set(data.username, socket.id, function (err, res) {
redisClient.set(data.username, socket.id);
});
}
else {
redisClient.del(data.username);
redisClient.set(data.username, socket.id, function (err, res) {
redisClient.set(data.username, socket.id);
});
}
});
socket.emit('login', {
result : true,
id : value.id,
registeredMobileNumber: value.registeredMobileNumber
});
} else {
socket.emit('login', {result: false});
}
});
});
socket.on('userConnected', function (username) {
redisClient.exists(username, function (err, doesExist) {
if (err) return;
if (!doesExist) {
redisClient.set(username, socket.id, function (err, res) {
redisClient.set(username, socket.id);
});
}
else {
redisClient.del(username);
redisClient.set(username, socket.id, function (err, res) {
redisClient.set(username, socket.id);
});
}
});
});
socket.on('disconnectUser', function (data) {
redisClient.exists(data.username, function (err, doesExist) {
if (err) return;
if (doesExist) {
redisClient.del(data.username);
}
});
});
server.listen(port, function () {
console.log('Express and socket.io listening on port ' + port);
});
}
You can use socket.on('disconnect', function() {});
When a User disconnects , save the users user_id.
Subsequent message on the user_id would be saved in the server.
When the client reconnects again get the time of the latest message and then push the message after that time (saved in the server) to the client.
Here's the server code:
io.set('authorization', socketioJwt.authorize({
secret: jwtSecret,
handshake: true
}));
app.post('/login', function (req, res)
{
if((req.body.username === "test") && (req.body.password === "test"))
{
var token = jwt.sign({ username: req.body.username, password: req.body.password}, jwtSecret, { expiresInMinutes: 60*24*7 });
res.json({ token: token });
console.log(req.body.username + " logged in");
}
else
{
res.status(401).send('Wrong user or password');
}
});
io.on('connection', function(socket)
{
// test event
socket.on('ping', function (data)
{
io.emit("pong", data)
});
});
And here the client:
var server = $("#server-input").val();
var obj = { username: $("#login-input").val(), password: $("#password-input").val() };
$.post(server + "/login", obj)
.done( function(response)
{
connect_socket(response.token);
});
function connect_socket(token)
{
socket = io(server, {query: 'token=' + token});
socket.on("connect", function()
{
socket.emit("ping", {hi:"there"});
socket.on("pong", function(data)
{
console.log(data);
});
});
}
Now, when one user connects, the ping gets send and is received. When the same user connects a second time, the pong will be received on both instances, but when another user connects the pong will only be reveiced by that user. User a and b don't see eachother.
How can I fix this? Could this be a problem with the authentication?