I want to build a chat system in nodeJs + MYSQL using php. It will be private chat one to one and will save chat in database. Anyone know from where I need to start.
Currently I got this code for SERVER:
var app = require('express').createServer()
var io = require('socket.io').listen(app);
app.listen(8181);
// routing
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
// usernames which are currently connected to the chat
var usernames = {};
io.sockets.on('connection', function (socket) {
// when the client emits 'sendchat', this listens and executes
socket.on('sendchat', function (data) {
// we tell the client to execute 'updatechat' with 2 parameters
io.sockets.emit('updatechat', socket.username, data);
});
// when the client emits 'adduser', this listens and executes
socket.on('adduser', function(username){
// we store the username in the socket session for this client
socket.username = username;
// add the client's username to the global list
usernames[username] = username;
// echo to client they've connected
socket.emit('updatechat', 'SERVER', 'you have connected');
// echo globally (all clients) that a person has connected
socket.broadcast.emit('updatechat', 'SERVER', username + ' has connected');
// update the list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
});
// when the user disconnects.. perform this
socket.on('disconnect', function(){
// remove the username from global usernames list
delete usernames[socket.username];
// update list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
// echo globally that this client has left
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
});
})
There are two ways. Thirst you can hold references to all sockets in an array (all at least IDs of these sockets). When a user emits private message you search the array for target socket and send it to this particular one. This requires to hold some kind of ID of a socket. You may use inner socket.id but it will be a problem when the client reconnects (new ID generated). And there is another problem when your app works on more then one machine (they cannot share arrays of connected clients).
The second way is to use rooms. Whenever client connects I suppose he has a name, for example John. Then you can use something like this for his connection:
socket.join('/priv/'+name);
Now this creates a room and adds socket to it. If you want to send message to John then you simply use
io.sockets.in('/priv/John').emit('msg', data);
At that point you can be sure that the message went exactly to the socket in /priv/John room. This works perfectly with Redis combined with socket.io (to avoid many machines problem) and session authorization. I didn't try it with memoryStore, but it should work as well.
Also you don't have to worry about rooms when clients disconnect. Socket.io automatically destroys empty rooms.
Related
This is a pretty simple question but i want to make sure that i am scaling our socket.io implementation correctly. We are using socket.io to respond back to the client after a lengthy process on the nodejs backend. So basically client makes call, then socket.io signals the client that the process has completed. Also socket.io ONLY responds to a temporary room that was established for the request.
In nodejs i created a global variable for the following so that i could emit back to the client room:
global.io = require('socket.io')(server);
But to create the room itself I am a little unsure how to create it globally such that only the socket that connected and made the request receives the response.
So if i have 500 client machines that initiate a connection through socket.io, each one will have its own socket. To ensure that the rooms are unique i use a guid across all 500. Of course i do not want all sockets to receive traffic if only one socket for a specific room is supposed to be evaluating the emit....
any ideas?
If I understood your question correctly, you're looking to send information to that 1 socket?
Perhaps something like this:
socket.broadcast.to(socketId).emit('someeventname', eventData);
If you have the connection open with that client, that means you have their socket id through socket.id . You can emit events to just that socket.
const app = express();
var http = require("http");
var server=http.createServer(app).listen(2525, (req, res) => {
console.log("Server running on", 2525);
});
var socketIO = require("socket.io");
var io = socketIO(server);
global.io = io
io.on("connection", async (socket) => {
socket.on("joinrooms", async (data) => {
socket.join(data.userId);
});
socket.on("sendMessage", async (data) => {
console.log("message", data);
io.to(data.touserId).emit("sendMessage", data);
});
});
/* Must Read section
Joinrrom data sample
data={
userId:123 //User's unique id.
}
sendMessage data sample
data={
userId:123, //sender User's unique id.
touserId:456, //reciver User's unique id.
}
Here I'm creating a room from the user's unique id(stored in DB) so whenever I
want to send data to a particular user I will emit an
event("io.to(data.touserId).emit") using the user's
a unique id that way only specific users will get messages.
*/
what I want to do is be able to create a room from the client but as if it was an object. For example:
I have a class called "room", this class has let's say 3 events implemented so when you create a new room you can trigger those events in each.
Is this possible? Here's my server code:
var express= require('express');
var app= express();
var server=require('http').createServer(app);
var io=require('socket.io')(server);
var channel= io.of('/arduino');
var bodyParser= require('body-parser');
server.listen(80, function(){
console.log("Server corriendo en puerto 80");
});
io.on('connection', function(socket){
console.log("Se conecto alguien por Socket");
socket.on('new-message', function(data) {
console.log(data);
console.log("Nuevo mensaje");
socket.emit('messages', data);
});
socket.on('JSON', function (data) {
var jsonStr = JSON.stringify(data);
var parsed = ParseJson(jsonStr);
console.log(parsed);
});
});
channel.on('connection', function(socket){
console.log("Se conectaron al canal 'arduino' ");
socket.on('new-message', function(data){
console.log("Sensor:");
console.log(data);
channel.emit("messages", data);
});
});
app.use(bodyParser.json());
app.get('/',function(req,res){
console.log('555555555');
res.status(200).send('GET TEST');
});
app.post('/',function(req,res){
console.log("post TEST");
datos=req.body;
console.log(datos);
res.end("fin");
});
Knowing the difference between rooms and namespaces is important for deciding what to go with :
namespaces are connected to by the client using io.connect(urlAndNsp)
(the client will be added to that namespace only if it already exists
on the server)
rooms can be joined only on the server side (although creating an API
on the server side to enable clients to join is straightforward)
You can define socket events specific for a namespace, but the client wont be able to connect if that namespace hasn't been defined previously in the server-side.
Read More : Rooms vs Namespaces
I think that the easiest solution for handling different channels, including dynamic channel names (created from the client) is to use rooms
Room Join/Leave from Client :
A socket.io-client can be subscribed to multiple rooms, I gave an answer with a method for :
Making server.side room subscribe/unsubscribe events that can be triggered from the client
It also explains how to get correctly the list of rooms a disconnecting client was subscribed to so a message to those rooms may be delivered to notify the client left.
I'm using socket.io on top of Express/Node
Right now I'm using this method:
// get room name from URL
var requestRoom;
app.get('/editor/:room', function(req, res) {
requestRoom = req.params.room;
res.render('editor/editor', {
title: 'Editor'
});
});
// use that room name for private rooms within socket.io
io.on('connection', function(socket) {
socket.join(requestRoom);
// rest of socket.io code....
to grab the room-name from a URL that looks like sitename.io/editor/[room-name].
Then I'm using requestRoom as a variable for socket.to(requestRoom).broadcast.. for sending the socket packets to a specific room.
The problem
Each time a new user visits the app, requestRoom gets repopulated for all users, instead of just for the user that just joined.
Is there a way to start a new 'instance' with own variables for each 'session'?
What am I missing here?
You cannot create new instance for each session.
But, you can solve this issue by sending the room name from the client.
When you connect to WebSocket using socket.io-client, add the room name to query parameter.
Client:
var requestRoom = "roomName";
io('/', {query: "requestRoom=" + requestRoom})
Server:
io.on('connection', function(socket) {
var requestRoom = socket.request._query.requestRoom;
socket.join(requestRoom);
// rest of socket.io code....
EDIT:
Nicholas Kyriakides used simpler way to solve this.
Server:
io.on('connection', function(socket) {
var refererSplit = socket.request.headers.referer.split('/');
var requestRoom = refAr[refererSplit.length-1]; //Considering Room name is at the end of the path.
socket.join(requestRoom);
// rest of socket.io code....
No need to pass roomName from the client.
I am not sure I understand the server side configuration of the socket.
var app = express();
var server = require('http').createServer(app);
var socketio = require('socket.io')(server, {
serveClient: config.env !== 'production',
path: '/socket.io-client'
});
Here, the code creates a socket server "attached" with the http server according to the api reference. What is attach?
socketio.on('connection', function (socket) {
socket.on('create', function(room) {
console.log('joining a room');
socket.join(room);
console.log('socket joined room: ', room);
});
socket.address = socket.handshake.address !== null ?
socket.handshake.address.address + ':' + socket.handshake.address.port :
process.env.DOMAIN;
socket.connectedAt = new Date();
// Call onDisconnect.
socket.on('disconnect', function () {
onDisconnect(socket);
console.info('[%s] DISCONNECTED', socket.address);
});
// Call onConnect.
onConnect(socket);
console.info('[%s] CONNECTED', socket.address);
});
};
Question: Here, the 'socket' variable is server's or client's ? if it is server's then why the socket.join(room) works ? (client's been added to a room) If it is client's, then why it has to listen to 'create' event.(client emits an event called create to change the room.)
to conclude, I confused by the three 'socket' in the following code.
socketio.on('connection', function (socket) {
socket.on('create', function(room) {
console.log('joining a room');
socket.join(room);
console.log('socket joined room: ', room);
});
});
Here, the code creates a socket server "attached" with the http server according to the api reference. What is attach?
webSocket connections (which socket.io is built on top of) all get initiated from the client by first making an HTTP connection. Thus, there must be an HTTP server that can be used for socket.io connections. That's why the initialization of socket.io needs an HTTP connection. Often, that web server is also acting as a normal web server too and thus can be used for both purposes. This simplifies cross-origin issues since all browsers all clients to connect to the same origin from which their web page was served. If, you don't already have another web server, socket.io can create it's own.
Question: Here, the 'socket' variable is server's or client's ?
It is the server-side object that represents the connection to a particular client. Socket.io has a socket object on the client that represents the client-side connection to the server and it has a socket object on the server (for each separate client connection) that represents the connection to a particular client.
So, if you want to send data to a particular client, you use the socket object that represents that client and you do:
socket.emit(msg, data);
if it is server's then why the socket.join(room) works ?
This works because this socket object represents the connection to a particular client. It is a server-side object, but it is specific to a particular client (e.g. there's a different one for each client connection).
to conclude, I confused by the three 'socket' in the following code.
socketio.on('connection', function (socket) {
socket.on('create', function(room) {
console.log('joining a room');
socket.join(room);
console.log('socket joined room: ', room);
});
});
socketio represents the overall socket.io module and is how you execute global commands. In this case, you are executing a global command to listen for any newly connection clients.
When you get a connection event, the argument to that event is the newly created socket object (a server-side object that represents a connection to a particular client).
The socket.on('create', ...) is a listener for the create message sent from the client to the server. So, this line of code says to listen for a create message sent from this particular (newly connected) client and when that message arrives, call a callback and pass it the data sent with the message (in this case a room name).
The socket.join(room); line uses the same socket as above and joins it to a specific room on the server.
is there any working example which store sessions and use that sessions for all opened windows to let one user just connect to one room just once?
this app will get phpsessions as node.js sessions but i cant findout how to just let one person to access this chat application just once
//define what we need
var express = require('express'),
app = express(),
memcache = require("memcache"),
http = require('http'),
server = http.createServer(app),
io = require('socket.io').listen(server),
co = require("./cookie.js"),
php = require('phpjs'),
codein = require("node-codein");
// answer all request from users and send them back index.html for root access
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
var cookieManager = new co.cookie(req.headers.cookie);
//using memcache as our session store
var client = new memcache.Client(11211, "localhost");
//connect to memcache client
client.connect();
//get our cookie sessions
user = client.get("sessions/"+cookieManager.get("sec_session_id"), function(error, result){
var session = JSON.parse(result);
//get just username from sessions(sessions store name and family and chat username in this case)
user = JSON.parse(session.name);
user = user.username;
//use this function to pass our chat username to our function
storeUsername(user);
});
});
function storeUsername(user){
// usernames which are currently connected to the chat
var usernames = {};
io.sockets.on('connection', function (socket) {
usernames[socket.id] = socket;
// when the client emits 'sendchat', this listens and executes
socket.on('sendchat', function (data) {
// we tell the client to execute 'updatechat' with 2 parameters
io.sockets.emit('updatechat', socket.username, data);
});
// when the client emits 'adduser', this listens and executes
socket.on('adduser', function(username){
// we store the username in the socket session for this client
socket.username = user;
// add the client's username to the global list
// echo to client they've connected
if(php.in_array(socket.username,usernames)){
delete usernames[socket.username];
}else{
usernames[user] = user;
console.log('not exist');
socket.emit('updatechat', 'SERVER', 'you have connected');
// echo globally (all clients) that a person has connected
socket.broadcast.emit('updatechat', 'SERVER', username + ' has connected');
// update the list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
}
});
// when the user disconnects.. perform this
socket.on('disconnect', function(){
// remove the username from global usernames list
delete usernames[socket.username];
// update list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
// echo globally that this client has left
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
});
});
}
server.listen(3000);
everything works fine and user which send data is defined but when i access this site in another tab i will connect another time to socket.io server
Do you mean sharing a websocket between browser tabs?
Sharing websocket across browser tabs?
But why do you need to share sockets? I've developed a node.js chat for a forum and we don't care about how many sockets a user has. We just have a "User" object that has a list of sockets. We don't care if the sockets come from firefox, from an android app... it's not a problem. And when we need to send information to a user, we send it to each socket.
You can try this. It is using express and socket.io https://github.com/gravityonmars/Balloons.IO