When making requests in Node.JS to another HTTP server, you can listen for when the server closes the connection with request.on('close', function(){});. I've been searching for similar functionality within Node's http.createServer(). I assumed that that request variable passed in to the callback had an on() function which I could call to listen for when the client closes the connection.
var http = require('http');
http.createServer(function(req, res) {
req.on('close', function() { console.log("Connection closed"); });
}).listen(80, '127.0.0.1');
However, after reading the 0.4.9 docs on streams, I noticed that under the close event it said:
Not all streams will emit this. (For example, an incoming HTTP request will not emit 'close'.)
Is there a way to listen for client connection terminations from the server in Node?
Well, you could access the socket directly by doing:
req.socket.on('close', ...);
However, doing this inside a request listener will bind the event for every request. So instead you should bind to the connection event:
var server = http.createServer();
server.on('request', function(req, res) { ... });
server.on('connection', function(socket) {
socket.on('close', function() {
console.log(socket.remoteAddress + '\'s keep-alive session died!');
});
});
EDIT:
Maybe I should have mentioned, yes, technically the socket object for an http server will carry a reference to the response on the _httpMessage property. However, I would advise against using this as it is part of the internal API and is subject to change without any warning.
It would be better to add your own property to get a reference to the last request made:
server.on('request', function(req, res) {
req.socket.currentRequest = req;
});
server.on('connection', function(socket) {
socket.on('close', function() {
var req = socket.currentRequest;
console.log('Socket closed. Last request for: ' + req.url);
});
});
Socket.io
When you want to know if a socket has closed, which only makes sense when you are do a hanging requests(real-time) I advice you to use socket.io instead, which takes care of all this:
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
io.sockets.emit('this', { will: 'be received by everyone');
socket.on('private message', function (from, msg) {
console.log('I received a private message by ', from, ' saying ', msg);
});
socket.on('disconnect', function () {
sockets.emit('user disconnected');
});
});
The nice thing about socket.io is that it's support by every browser(major) without you having to worry about the little details(which make you go insane). They have abstracted that with a nice API.
Express:
Or when using Express
var app = require('express').createServer();
app.get('/', function(req, res){
req.on('close', function () {
console.log('closed');
});
});
app.listen(3000, '127.0.0.1');
Related
I'm trying some things with socket.io on NodeJS and I can't figure out how to trigger the the socket (only) from NodeJS.
Till now I was using socket.io by calling it from the front end but I wonder if is it possible to do the same thing I did on the front end but this time on the nodeJS part(server side).
My guess is it's not possible because is required a kind of connection(I like to call it a TCP connection,but I'm not sure if that's true or not) and without a second participant in the connection the socket won't work.That's my guess.
So what I'm doing now is :
app.js(server file)
...
const ioLib = require('./path/io.js')(io);
...
...
...
path/io.js(socket file)
module.exports = function(io){
io.on('connection', async function(socket) {
console.log('socket talks : a user connected');
...
...
});
module.exports.io = io;
}
And from an file.ejs file I do :
var socket = io("url");
So with this,let's call it schema,I do the following :
When I access that webpage the 'connection' event is triggered in the sockets.
My question is,and I'm trying to formulate it as simple as I can :
How can I do the same but without a webpage?Is it possible to trigger the sockets inside the NodeJS?
What do you think?
It is possible to connect a Socket.IO server from a stand alone Node.js application (does not really matter where it runs) rather than a web front-end, accessed via a web browser. In order to achieve this, you should use socket.io-client. An example client usage might be as follows:
// Node.js app: client.js
const io = require('socket.io-client');
const socket = io.connect('http://SERVER_IP:SERVER_PORT', {
reconnect: true
});
socket.on('connect', function (socket) {
console.log('Connected to the server!');
});
socket.emit('connected', 'Hi from the client side!');
In this case, your server side application should include something as follows:
// Node.js app: server.js
io.on('connection', function(socket) {
console.log('socket talks: a user connected');
// Print the message that comes from the socket client
socket.on('connected', function (msg) {
console.log(msg);
});
});
As you can see above, fundamentally, the architecture remains the same as server-client. Now, let's go one step further and put all those codes in a single js file, and see how it works:
// server/client together: crazy-socketapp.js
const io_server = require('socket.io').listen(3030);
io_server.sockets.on('connection', function (socket) {
console.log('A client is connected!');
socket.on('connected', function (msg) {
console.log(msg);
});
});
const io_client = require('socket.io-client');
const socket = io_client.connect('http://localhost:3030', {
reconnect: true
});
socket.on('connect', function (socket) {
console.log('Connected to the server!');
});
socket.emit('connected', 'Hi from the client side! ');
The output of the app:
> A client is connected!
> Connected to the server!
> Hi from the client side!
Hope this helps!
I've got the following setup (important bits only for brevity):
app.js
...
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
server.listen(port, function() {
console.log(`Server is listening on port: ${port}`);
});
io.on('connection', function (socket) {
console.log('connection');
});
const routes = require('./routes/index')(io, passport);
app.use('/', routes);
index.js (server)
router.get('/game/:id', isAuthenticated, (req, res) => {
if (req.id)
{
var game = Game.findOne({_id: req.id}, (err, obj) => {
io.on('getGameInfo', (socket) => {
io.emit('gameInfo', obj);
});
res.render('game', obj);
});
}
else
{
// Id not valid, do something
}
});
client:
const socket = io('http://localhost:3000');
socket.on('gameInfo', function(data) {
console.log(data);
}.bind(this));
socket.on('connect', () => {
socket.emit('getGameInfo');
});
So basically I want to emit a getGameInfo call once I know the client has connected, and the getGameInfo listener has been set up in the game route. But when I emit the getGameInfo from the client, the server callback isn't being hit. I'm not sure if I'm missing something obvious, or if this is a closure issue, or if I'm just having one of those days, or if I'm going about this entirely the wrong way.
There are multiple problems here. I'll start by showing the correct way to listen for an incoming socket.io message on the server:
io.on('connection', function (socket) {
// here's where you have a new socket and you can listen for messages
// on that socket
console.log('connection');
socket.on('gameInfo', (data) => {
socket.emit('gameInfo', obj);
});
});
Some of the issues:
On the server, you listen for messages via the socket object, not via the io object. So, you would typically add these event listeners in the io.on('connection', ...) handler because that's where you first see newly connected sockets.
You pretty much never want to add event listeners inside an Express route handler because that is called many times. In addition, at the moment the route handler is called, the browser has not yet received the page and will not yet be connected so even if this was an OK place to do stuff, the page is not yet connected anyway.
When you want to send a message back to just one connection, you send it with socket.emit(), not io.emit(). io.emit() broadcasts to all connected clients which I don't think is what you want.
I'd suggest you not overload the same message name for client and server to mean two different things as this can lead to confusion when reading code or if you ever share some code between client and server. You client is really sending a "getGameInfo" message and then your server responds with a "gameInfo" message that contains the gameInfo.
If, in a route handler, you want to .emit() to the socket from that page which it looks like you are trying to do, then you have to do some work to create a link between the session of the current page and the socket for that page. There are a number of ways to do that. If you're using any session middleware, you can record the socket in the session at the point the socket connects. Then, from your express routes, you can get that socket from the session object at any time.
I used websocket interface to connect to websocket server . what if i want send data that i receive from the websocket server through my websocket interface to client connected to me through http server , should i use socket.io ?
so at the end i will have socket.io attached to to http server and websocket interface to get data and in case of message come will be send to client through socket.io . is that the best setup ?
Code Example :
// Require HTTP module (to start server) and Socket.IO
var http = require('http'),
io = require('socket.io');
var WebSocket = require('ws');
var ws = new WebSocket('ws://localhost:5000');
// Start the server at port 8080
var server = http.createServer(function (req, res) {
// Send HTML headers and message
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.end('<h1>Hello Socket Lover!</h1>');
});
server.listen(8080);
// Create a Socket.IO instance, passing it our server
var socket = io.listen(server);
ws.on('open', function open() {
ws.send('something');
});
ws.on('message', function (data, flags) {
// here the data will be send to socket.io
});
// Add a connect listener
socket.on('connection', function (client) {
// Success! Now listen to messages to be received
client.on('message', function (event) {
console.log('Received message from client!', event);
});
client.on('disconnect', function () {
clearInterval(interval);
console.log('Server has disconnected');
});
});
Yes, your design is correct.
However, one thing that you should keep in mind is take care of sending the message to the correct client after authentication. In my opinion, it is very easy to make this mistake, partially because of the simplicity of messaging using websockets.
So I'm trying to broadcast Laravel 5 Events with the help of Redis. No I don't wanna use a service like Pusher since it's not free (even if the free limit would be enough for me) and I wanna keep control of the broadcast server.
So what I've done so far is, I'Ve set up a redis server (listening on port 6379 -> default), I've set up the following event:
class MyEventNameHere extends Event implements ShouldBroadcast
{
use SerializesModels;
public $data;
/**
* Create a new event instance.
*
* #return \App\Events\MyEventNameHere
*/
public function __construct()
{
$this->data = [
'power' => 10
];
}
/**
* Get the channels the event should be broadcast on.
*
* #return array
*/
public function broadcastOn()
{
return ['pmessage'];
}
}
I registered a route to that event:
Route::get('test',function()
{
event(new App\Events\MyEventNameHere());
return "event fired";
});
I've created (more like copied :P) the node socket server:
var app = require('http').createServer(handler);
var io = require('socket.io')(app, {origins:'*:*'});
var Redis = require('ioredis');
var redis = new Redis();
app.listen(6379, function() {
console.log('Server is running!');
});
function handler(req, res) {
res.writeHead(200);
res.end('');
}
io.on('connection', function(socket) {
console.log(socket);
});
redis.psubscribe('*', function(err, count) {
});
redis.on('pmessage', function(subscribed, channel, message) {
console.log(message);
message = JSON.parse(message);
io.emit(channel + ':' + message.event, message.data);
});
And I created the view to actually receive the broadcast (testview.blade.php):
#extends('layout')
#section('content')
<p id="power">0</p>
<script>
var socket = io('http://localhost:6379');
socket.on("pmessage:App\\Events\\MyEventNameHere", function(message) {
console.log(message);
$('#power').text(message.data);
});
console.log(socket.connected);
</script>
#endsection
I can launch the redis server without any problems.
I can launch the node socket.js server and I'm getting the response "Server running"
When I hit the route to the event I get the return "event fired" in my browser.
When I hit the route to the actual view
Route::get('test/view',function()
{
return view('testview');
});
I can see the whole page (layout is rendered), and the webconsole does not show any errors.
However if I fire the event, the view won't change, which means, the broadcast is not received right?
Now I included an output for the console
console.log(socket.connected);
which should show me if the client is connected to the socket.io right?
Well, the output says false. What am I doing wrong here?
Further information on my setup: I'm running the whole project on the php built-in server, the whole thing is running on Windows (if ever that could matter), my firewall is not blocking any of the ports.
EDIT 1:
I forgot to say that my node server is not receiving the messages as well... It only says "Server running", nothing else.
EDIT 2:
I used another socket.js:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html');
});
redis.subscribe('test-channel', function () {
console.log('Redis: test-channel subscribed');
});
redis.on('message', function(channel, message) {
console.log('Redis: Message on ' + channel + ' received!');
console.log(message);
message = JSON.parse(message);
io.emit(channel, message.payload)
});
io.on('connection', function(socket) {
console.log('a user connected');
socket.on('disconnect', function() {
console.log('user disconnected');
});
});
http.listen(3000, function() {
console.log('listening on *:3000');
});
And this time the console receives the messages.
So if the node socket.io receives the messages, then what's wrong with my client? Obviously the messages are being broadcasted correctly, the only thing is that they are not being received by the client...
I can't say what is exactly wrong and probably no one can't, because your problem is to broad and enviroment dependent. Using Wireshark Sniffer you can easily determinate part of solution that is not working correctly and then try find solution around actual problem.
If your question is about how to do that, I will suggest not involving node on server side and use .NET or Java language.
The problem with your code is you are connecting your client socket to the redis default port 6379 rather than the node port that is 3000.
So in your blade view change var socket = io('http://localhost:6379'); to var socket = io('http://localhost:3000');
have you tried to listen to the laravel queue, from command line, before to fire the event?
php artisan queue:listen
I'm trying to hookup a front-end server to our notification service, and then the front-end server to the browser. The notification service is realtime, and includes the recipients user id in the sent notification data. I'm not having problems with the notification service sending the data and the front-end server receiving it. And I can have it connect and disconnect to the browser just fine, like this:
io.on('connection', function (socket) {
console.log('connected');
socket.on('disconnect', function () {
console.log('disconnected');
});
});
I need to now know what user is signed in to the front-end server so I know which user/socket to send the notification to. We can get this data from our express session if I put the connection listener in a middleware function like so:
var allClients = {};
function useSocket (req, res, next) {
io.on('connection', function (socket) {
allClients[req.session.userId] = socket;
socket.on('disconnect', function () {
console.log('disconnected');
allClients[req.session.userId].removeAllListeners();
// we were also suggested things like socket.close() which did not help
});
});
next();
}
app.get('*', useSocket, function (req, res){
res.render('../views/index');
});
except the code above creates a new connection on every browser refresh, so a user gets a dupe notification for as many times as any browser has connected.
This is my code that talks to the notification service:
var notificationServiceSocket = require('socket.io-client').connect(notificationServiceUrl);
notificationServiceSocket.on('notification', function (data) {
// data.user_id is the intended recipient
});
How can I determine which socket is related to the recipient without creating infinite connections?