Creating a Socket IO channels from client side - node.js

I'm new to Nodejs and Socketio. I want to do something like the following.
-> Create a socket.io/node.js server that listens to channels specified by the web browser.
-> server side script pushes messeges to specific channels.
(I already have a server created and here is the code. This just sends out two messeges. action and messege to all the connected clients)
//start code
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, url = require('url')
app.listen(8080);
function handler (req, res) {
// parse URL
var requestURL = url.parse(req.url, true);
// if there is a message, send it
if(requestURL.query.message)
sendMessage(decodeURI(requestURL.query.action), decodeURI(requestURL.query.message));
// end the response
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end("");
}
function sendMessage(action, message) {
io.sockets.emit('notification', {'action': action, 'message' : message});
}
So basically Socket.io server doesn't have any channels defined inside them. But they are defined by the client (js in browser) and messeges are sent to the specific channels by a server side script (like php using cURL)
Sorry if this questions has been asked before, I did search and couldnt find anything helpful.

I am bit confused about your question. By channels, do you mean socket.io rooms?
https://github.com/LearnBoost/socket.io/wiki/Rooms
Rooms are groups of clients and it is possible to send message to all clients in room by using following command:
io.sockets.in('room').emit('event_name', data)
It is important to realize that rooms are server side thing. So, if you want to send message to a room from client, you must send message (or request) to server and pass room name.
Also, it is bit unusual that your handler function is exposed as HTTP endpoint. If your clients already have socket.io connections, than it is easier to send it as socket.io message.
If this does not answer you question, can you post also your client side code? Maybe it will help to understand me what are you trying to achieve.

Related

What is the proper way to emit an event with socket.io?

I want to emit an event to the client when a long fucntion comes to an end.
This will show a hidden div with a link - on the client side.
This is the approach i tested:
//server.js
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
require('./app/routes.js')(app, io);
//routes.js
app.post('/pst', function(req, res) {
var url = req.body.convo;
res.render('processing.ejs');
myAsyncFunction(url).then(result => {
console.log('Async Function completed');
socket.emit('dlReady', { description: 'Your file is ready!'});
//do some other stuff here
}).catch(err => {
console.log(err);
res.render('error.ejs');
})
});
I get this
ERROR: ReferenceError: socket is not defined
If i change the socket.emit() line to this:
io.on('connection', function(socket) {
socket.emit('dlReady', { description: 'Your file is ready!'});
});
Then i don't receive an error, but nothing happens at the client.
This is the client code:
<script>
document.querySelector('.container2').style.display = "none";
var socket = io();
socket.on('dlReady', function(data) { //When you receive dlReady event from socket.io, show the link part
document.querySelector('.container1').style.display = "none";
document.querySelector('.container2').style.display = "block";
});
</script>
This whole concept is likely a bit flawed. Let me state some facts about this environment that you must fully understand before you can follow what needs to happen:
When the browser does a POST, there's an existing page in the browser that issues the post.
If that POST is issued from a form post (not a post from Javascript in the page), then when you send back the response with res.render(), the browser will close down the previous page and render the new page.
Any socket.io connection from the previous page will be closed. If the new page from the res.render() has Javascript in it, when that Javascript runs, it may or may not create a new socket.io connection to your server. In any case, that won't happen until some time AFTER the res.render() is called as the browser has to receive the new page, parse it, then run the Javascript in it which has to then connect socket.io to your server again.
Remember that servers handle lots of clients. They are a one-to-many environment. So, you could easily have hundreds or thousands of clients that all have a socket.io connection to your server. So, your server can never assume there is ONE socket.io connection and sending to that one connection will go to a particular page. The server must keep track of N socket.io connections.
If the server ever wants to emit to a particular page, it has to create a means of figuring out which exact socket.io connect belongs to the page that it is trying to emit to, get that particular socket and call socket.emit() only on that particular socket. The server can never do this by creating some server-wide variable named socket and using that. A multi-user server can never do that.
The usual way to "track" a given client as it returns time after time to a server is by setting a unique cookie when the client first connects to your server. From then on, every connection from that client to your server (until the cookie expires or is somehow deleted by the browser) whether the client is connection for an http request or is making a socket.io connection (which also starts with an http request) will present the cookie and you can then tell which client it is from that cookie.
So, my understanding of your problem is that you'd like to get a form POST from the client, return back to the client a rendered processing.ejs and then sometime later, you'd like to communicate with that rendered page in the client via socket.io. To do that, the following steps must occur.
Whenever the client makes the POST to your server, you must make sure there is a unique cookie sent back to that client. If the cookie already exists, you can leave it. If it does not exist, you must create a new one. This can be done manually, or you can use express-session to do it for you. I'd suggest using express-session because it will make the following steps easier and I will outline steps assuming you are using express-session.
Your processing.ejs page must have Javascript in it that makes a socket.io connection to your server and registers a message listener for your "dlready" message that your server will emit.
You will need a top-level io.on('connection', ...) on your server that puts the socket into the session object. Because the client can connect from multiple tabs, if you don't want that to cause trouble, you probably have to maintain an array of sockets in the session object.
You will need a socket.on('disconnect', ...) handler on your server that can remove a socket from the session object it's been stored in when it disconnects.
In your app.post() handler, when you are ready to send the dlready message, you will have to find the appropriate socket for that browser in the session object for that page and emit to that socket(s). If there are none because the page you rendered has not yet connected, you will have to wait for it to connect (this is tricky to do efficiently).
If the POST request comes in from Javascript in the page rather than from a form post, then things are slightly simpler because the browser won't close the current page and start a new page and thus the current socket.io connection will stay connected. You could still completely change the page visuals using client-side Javascript if you wanted. I would recommend this option.

Initiate data transfers from Server => Client?

i want to build a server app in node/express which receives requests from one client and sends information to another client, maybe somehow similar to this:
var express = require('express');
var app = express();
var IP_client1 = "10.1.2.3";
var IP_client2 = "10.1.2.4";
//get information from client1
app.get('/client1',function(req,res){
res.send('OK, will send it to client2");
//==> How to send to client2 from here?
});
//get information from client2
app.get('/client2',function(req,res){
res.send('OK, will send it to client1");
//==> How to send to client1 from here?
});
//==> How to send data to client1 or/and client2 from here?
app.listen(3000);
I know how to handle requests from clients to the server, but how could i simply set up a transfer of data from the server to another arbitrary client?
I went through a lot of tutorials, but no one covers this!
Yes, websockets (sockets.io) is what allows free communication through communication channels (sockets).
The tutorial here
https://socket.io/get-started/chat/
showed me what to do, but unfortunately there seems to be a little bug (or missing detail) in the clients html file!?
It needs:
var socket = io.connect('http://local.host:port');
to make it running! Very annoying!
Anyway, i have a better feeling now, and i can step forward to the next problem!

Using Socket.io with Sequelize

I am trying to use Socket.io and Sequelize to create a chat app. Socket.io will handle the socket to allow for instant messaging. Sequelize will handle storing the messages so when you refresh the screen you still have your messages.
What is happening is that on localhost my socket works, but it does not send the messages to the database. When I put it onto Heroku, my database worked, but it does not use the sockets.
My socket is located in app.js and my database route is located in routes/messages.js.
I have been working on this bug for a while now and I have been trying to get help with it. I think the best way to share this is with my markdown I created detailing my efforts to fix my bug that can be found at here. My repo for this can be found here.
There are a few different parts that you need to distinguish:
the HTTP server, in your code represented by the variable http
the Express app, represented by app
the Socket.IO server, represented by io
a Socket.IO (client) connection (see below)
The HTTP server directs "normal" HTTP requests to the Express app, which will handle them according to the middleware and routes that are set up. A router handler gets called with (at least) two arguments, generally called req and res, to represent the (incoming) HTTP request and the (outgoing) HTTP response.
The Socket.IO server gets to handle specific Socket.IO requests, which get sent to the server by the Socket.IO client (running in the browser). When such a client sets up a connection with the server, the connection event gets triggered on the server. Any handlers for this event will get passed an argument, generally called socket, that represents the (bidirectional) connection with that client.
That Socket.IO connection can receive messages (sent from the client running in the browser), which trigger events on the socket. You can install a handler to listen for particular messages (like "chat message"), which will receive, as argument, the data that was sent to it by the client.
The issue in your code seems to be with setting up everything to handle those chat messages. The correct setup order would be:
listen on the Socket.IO server for connection events
when such an event is received, add a listener for the chat message event on the connection
when such an event is received, write the data to the database.
In code:
// Listen for new client connections.
io.on('connection', function(socket) {
// Listen for the client to send a _"chat message"_ message.
socket.on('chat message', function(data) {
// Store the data in the database.
models.Messages.create({
message : data.message,
username : data.username
});
});
});
As you can see, req and res aren't available inside of those Socket.IO event handlers, because those are only used for normal HTTP requests.
Also, as opposed to HTTP, you don't necessarily have to send anything back to the client when you have received a message, so I left that part out. The handler above only writes the message data to the database (it also doesn't check for, or handle, errors, which eventually you should add).

How to inform a NodeJS server of something using PHP?

I'd like to add a live functionality to a PHP based forum - new posts would be automatically shown to users as soon as they are created.
What I find a bit confusing is the interaction between the PHP code and NodeJS+socket.io.
How would I go about informing the NodeJS server about new posts and have the server inform the clients that are watching the thread in which the post was posted?
Edit
Tried the following code, and it seems to work, my only question is whether this is considered a good solution, as it looks kind of messy to me.
I use socket.io to listen on port 81 to clients, and the server running om port 82 is only intended to be used by the forum - when a new post is created, a PHP script sends a POST request to localhost on port 82, along with the data.
Is this ok?
var io = require('socket.io').listen(81);
io.sockets.on('connection', function(socket) {
socket.on('init', function(threadid) {
socket.join(threadid);
});
});
var forumserver = require('http').createServer(function(req, res) {
if (res.socket.remoteAddress == '127.0.0.1' && req.method == 'POST') {
req.on('data', function(chunk) {
data = JSON.parse(chunk.toString());
io.sockets.in(data.threadid).emit('new-post', data.content);
});
}
res.end();
}).listen(82);
Your solution of a HTTP server running on a special port is exactly the solution I ended up with when faced with a similar problem. The PHP app simply uses curl to POST to the Node server, which then pushes a message out to socket.io.
However, your HTTP server implementation is broken. The data event is a Stream event; Streams do not emit messages, they emit chunks of data. In other words, the request entity data may be split up and emitted in two chunks.
If the data event emitted a partial chunk of data, JSON.parse would almost assuredly throw an exception, and your Node server would crash.
You either need to manually buffer data, or (my recommendation) use a more robust framework for your HTTP server like Express:
var express = require('express'), forumserver = express();
forumserver.use(express.bodyParser()); // handles buffering and parsing of the
// request entity for you
forumserver.post('/post/:threadid', function(req, res) {
io.sockets.in(req.params.threadid).emit('new-post', req.body.content);
res.send(204); // HTTP 204 No Content (empty response)
});
forumserver.listen(82);
PHP simply needs to post to http​://localhost:82/post/1234 with an entity body containing content. (JSON, URL-encoded, or multipart-encoded entities are acceptable.) Make sure your firewall blocks port 82 on your public interface.
Regarding the PHP code / forum's interaction with Node.JS, you probably need to create an API endpoint of sorts that can listen for changes made to the forum. Depending on your forum software, you would want to hook into the process of creating a new post and perform the API callback to Node.js at this time.
Socket.io out of the box is geared towards visitors of the site being connected on the frontend via Javascript. Upon the Node server receiving notification of a new post update, it would then notify connected clients of this new post and its details, at which point it would probably add new HTML to the DOM of the page the visitor is viewing.
You may want to arrange the Socket.io part of things so that users only subscribe to specific events being emitted by them being in a specific room such as "subforum123" so that they only receive notifications of applicable posts.

Generating socket.io at Server side as Rest request

There is a way to manage the socket.io creation at Server Side?, Currently, I couldn't found any doc, Only found in relation with the socket is created per request from a client "io.connect(server)".
The current flow work OK:
Set Socket.io (at Node.js) at SERVER:PORT
Client connect to SERVER
using io.connect(SERVER:PORT)
I wonder if it is possible ? Trying to do:
Set Socket.io (at Node.js) at SERVER:PORT
Recieved a POST (REST) - Server side
Create/Open Socket.io a server side.
At response of Post send the id?
the clien open a socke.io
Sent to client socket.id to client as
So Far, looking in deep on the code and doc, I found that socket.io support namespaces, so I used this in order to manage client connection id.
at server.js
var app = express();
var server = require('http').createServer(app),
io = require('socket.io').listen(server,{ log: false });
// Rest New Process
function generateNameSpaceWs (responce, request) {
io.of("/" + id).on('connection', handler);
response.send(id);
}
app.post("/newWS", function (res, req) {
return generateNameSpaceWs(res, req);
}
at Client.js
function makeWS(){
var ws, c = new XMLHttpRequest();
c.open("GET", url, false);
c.send();
if (c.status == 200){
id = JSON.parse(c.responseText);
ws = new io.connect("server/" + id)
}
So far you are doing right, if I understand your question correctly, you are trying to authenticate connection via POST, so that user can only connect to server via socket if server responds to ID. This is a roundabout way. Use the socket instead of POST.
Socket server has to be running already, and accepts connection via io.sockets.on('connection'), at server you can choose whether to accept it or reject it ,do socket.disconnect('unauthorized') to close connection from server.
I would you suggest you do this :
Set Socket.io (at Node.js) at SERVER:PORT
Client connect to SERVER using io.connect(SERVER:PORT)
Send what you are sending in POST over socket.
Authenticate/Process on io.sockets.on('connection', function(socket) at server.
Close socket if unathorized.
Send back ID data to client.
This doesn't seem possible -- while the official documentation for socket.io is lacking, the documentation for the net module indicates that the only way to create a socket is to initiate it server side.
However, you can still achieve the desired effect by creating an id for the socket on the server to associate with the socket. That is,
Set Socket.io (at Node.js) at SERVER:PORT
Recieved POST (REST) - Server side
Create id (Note:This could be done before step 2)
At response of Post send the id!
Client connect to SERVER
using io.connect(SERVER:PORT)
The client sends the id to the server using something like
socket.emit("set_id",id)
The server recieves the id and associates it with the socket using something like
socket.on("set_id",function(id){
socket.set("id",id)
}
Now you can reference the socket using the id that you created!
Good luck!

Resources