New to Node.js. Searched lots of answers in SO, but could not find appropriate solution for this issue. I'm using custom memcache server code for node.js for getting data from memcached service (stored using php), added only relevant code below:
var net = require("net");
net.createServer(function(socket) {
socket.setEncoding('utf8');
var cmd = {};
var incoming = '';
socket.addListener('receive', function(data) {
incoming += data;
// in the mid of receiving some data, so we can't
// parse it yet.
if(data.substring(data.length - 2) != '\r\n')
return;
data = incoming;
incoming = '';
var parts = data.split(/ /);
if(parts[0].match(/^(add|set|delete|get|gets)$/i)) {
switch(parts[0]) {
case 'add':
var message = JSON.parse(data.split('\r\n')[1]);
self._add.call(socket, parts[1].replace('\r\n', ''), message);
break;
case 'set':
var message = data.split('\r\n')[1];
try {
message = JSON.parse(message);
} catch(e) {}
var subparts = parts[1].replace('\r\n', '').split('/');
self._set.call(socket, subparts.slice(0, 2), subparts.slice(2).join('/'), message);
break;
case 'delete':
self._delete.call(socket, parts[1].replace('\r\n', ''));
break;
case 'get':
self._get.call(socket, parts[1].replace('\r\n', ''));
break;
case 'gets':
var getsparts = parts.slice(1);
getsparts[getsparts.length - 1] =
getsparts[getsparts.length - 1].replace('\r\n', '');
self._gets.call(socket, getsparts);
break;
}
}
});
}).listen(11211, "localhost");
But whenever i try to start the node server, it says EADDRINUSE, as this is due to the reason that memcached service is already running on port 11211. If i change the port to something else such as (51242) then the error does not show up, but no data is retrieved from memcached.
So is there any way i can connect to memcached service using same port 11211 from this node.js memcache server code..?
I'm new to Node.js, so could not figure out any posible solution. So, how can i fix this issue without using any node.js memcached modules, or am i completely on the wrong path..? As a side note I'm running memcached service and nodejs on windows 7. Please suggest
Update
Instead of
net.createServer(function(socket) {
I changed to following for test
this.start = function() {
var socket = net.createConnection(11211, "127.0.0.1");
console.log('Socket created.');
socket.on('data', function(data) {
console.log('RESPONSE: ' + data);
}).on('connect', function() {
socket.write("GET / HTTP/1.0\r\n\r\n");
}).on('end', function() {
console.log('DONE');
});
}
but on console, it shows Socket Created, and then throws error:: Error connect ECONNREFUSED
This is happening because node should be interacting with memcached, not as a server (which binds the ports and accepts incoming connections), but as a client. Look into net.createConnection instead of net.createServer.
Related
I use php(laravel 5.8) to broadcast, and I use laravel-echo-server, it's work. But!!! recent I need to something by my self. And I write a socket service by ndoejs. This is my code
const jwt = require('jsonwebtoken');
const server = require('http').Server();
const io = require('socket.io')(server);
...
// ==== The question is here =====================
const Redis = require('ioredis');
const redis = new Redis({
port: REDIS_PORT,
host: REDIS_HOST,
password: REDIS_PASSWORD
});
redis.psubscribe('myTestChannel.*');
redis.on('pmessage', function(pattern, channel, message) {
console.log(channel, message);
const object_message = JSON.parse(message);
io.sockets.emit(channel, object_message.data);
});
// ==== The question is here =====================
io.sockets.use(function (socket, next) {
if (socket.handshake.query && socket.handshake.query.token){
// auth
} else {
next(new Error('Authentication error'));
}
}).on('connection', function(socket) {
console.log('==========connection============');
console.log('Socket Connect with ID: ' + socket.id);
socket.on('join', (data) => {
... do something
});
socket.on('disconnect', () => {
... do something
})
});
server.listen(LISTEN_PORT, function () {
console.log(`Start listen ${LISTEN_PORT} port`);
});
It's work, but running a long time, my php get a error message, phpredis read error on connection. I'm not sure the real reason. But I guess is about my socket, because I use laravel-echo-server is great.
I'm not sure my redis.psubscribe's position is right? Maybe this cause a long time connection and cause php read error on connection?
I should move the redis.psubscribe into on('connection') and unsubscribe when disconnection?
I want to know the redis.psubscribe is the main cause the problem? Thanks your help.
I am developing a website which uses a private messaging system using php + socket.io.
From the beginning i passed the sender_id, recipient_id and text to socket.io using socket.emit but later realized that this could be easily tampered with and wanted to use my php sessions in some way to be sure that the sender_id is indeed the sender_id.
I have the following setup right now but i dont really understand how to pass the session from index.php to app.js and then connect to redis-server in app.js to get the PHPSESSID which holds the user_id.
Server 1 running nginx + php-fpm (index.php)
Server 2 running node.js with socket.io (app.js)
Server 3 running redis for session management
My code right now looks like the following but is obviously missing the redis part right now which i would really appriciate some help with.
Thanks!
index.php
<?php
session_start();
if ($_SESSION['user_id'] == false){
header("Location:login.php");die;
}
?>
<script>
var socket = io('https://app01.dev.domain.com:8895');
socket.on('connect', function(){
console.log("Connected to websockets");
});
socket.on('event', function(data){});
socket.on('disconnect', function(){});
$('.chat-message').keypress(function (e) {
if (e.which == 13) {
console.log("send message");
var friend_id = $(this).attr('id');
friend_id = friend_id.split("-");
friend_id = friend_id[3];
var obj = {
recipient_id: friend_id,
text: $(this).val()
};
socket.emit('chat_message', obj);
$(this).val('');
return false;
}
});
</script>
app.js
var https = require("https"), fs = require("fs");
var options = {
key: fs.readFileSync('/etc/letsencrypt/live/domain/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/domain/cert.pem'),
ca: fs.readFileSync('/etc/letsencrypt/live/domain/chain.pem')
};
var app = https.createServer(options);
var io = require("socket.io")(app);
var redis = require("redis");
// This i want to fill with for example PHPSESSION:user_id that i get from redis and later use it as sender
// var all_clients = {};
io.set("transports", ["websocket", "polling"]);
io.on("connection", function(client){
console.log("Client connected");
// Here i would like to connect to redis in some way and get the user_id but dont really understand how
//all_clients[USER_ID_FROM_REDIS] = client.id;
//var user_id = USER_ID_FROM_REDIS;
client.on("chat_message", function(data){
var obj = {
to: data.recipient_id,
text: data.text
};
console.log("Message inbound from socket: "+client.id+" from: "+data.user_id+" to: "+data.recipient_id+" with text: "+data.text);
});
client.on("disconnect", function(){
console.log("Client disconnected ");
//delete all_clients[USER_ID_FROM_REDIS];
});
});
app.listen(8895, function(){
console.log("listening on *:8895");
});
var recursive = function () {
//console.log("Connected clients: "+Object.keys(all_clients).length);
//console.log(JSON.stringify(all_clients));
setTimeout(recursive,2000);
}
recursive();
HTTP in itself does not protect against MITM attacks, to protect against MITM the server certificate needs to be pined.
To protect against a user being spoofed you need authentication such as logging-in or a secret token like Dropbox.
Add certificate pinning, that is just jargon for validating that you are connecting to the correct server and not a MITM by verifying the certificate that is sent by the server. MITM used to be harder but WiFi has made it easy to connect to the wrong end-point at Hot Sports, even at home I have seen this.
My application stack:
On my server runs a Redis server. The PHP backend communicates with Predis library with the Redis server. It will publish messages. These messages will be fetched by my Redis client (node.js) and pushed to the connected websocket clients (with SockJS).
My problem:
It runs well. At least for broadcast messages. Now I came to the point I need to send a unicast message and I'm stuck... How to connect the user on the backend side (sender of messages) with the connected client of the websocket?
Code snippets:
PHP
$redis = new Client();
$redis->publish('updates', Random::getUniqueString());
Redis client on node.js server
redis.subscribe('updates');
redis.on('message', function(channel, data) {
for (var id in sockets) {
if (sockets.hasOwnProperty(id)) {
sockets[id].write(data);
}
}
});
SockJS client
mySocketFactory.setHandler('message', function(event) {
console.log(event.data);
});
Like I said. Working well but the id used for the socket connection is not known by the PHP backend.
Edit: One idea I got in mind is to use cookies.
I found a way to solve my problem. When the socket connection is established I sent a request to my PHP backend and ask for the user id. This is stored on the node.js server. When messages are incoming there is a check if they are for specific user and handle them only for them.
So, what do I store exactly on my node server?
var sockets = {}; // {connection_id: socket_connection}
var connIdToUser = {}; // {connection_id: user_id}
var connIdsForUser = {}; // {user_id: [connection_id_1, connection_id_2 ,...]}
socketServer.on('connection', function(conn) {
sockets[conn.id] = conn;
var options = {
host: os.hostname(),
port: 80,
path: '/user/id',
method: 'GET'
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
var userId = JSON.parse(chunk).id;
connIdToUser[conn.id] = userId;
if (!connIdsForUser.hasOwnProperty(userId)) {
connIdsForUser[userId] = [];
}
connIdsForUser[userId].push(conn.id);
console.log('connection id ' + conn.id + ' related to user id ' + userId);
});
});
req.end();
conn.on('close', function() {
console.log('connection lost ' + conn.id);
// remove connection id from stack for user
var connections = connIdsForUser[connIdToUser[conn.id]];
var index = connections.indexOf(conn.id);
if (index > -1) {
connections.splice(index, 1);
}
// remove connection at all
delete sockets[conn.id];
// remove relation between connection id and user
delete connIdToUser[conn.id];
});
});
The reason for storing the relation between user id an connection id twice is the different use case I need either for sending a message or deleting the connection for the close event. Otherwise I would have to use a nested loop.
As you can see deleting a socket is fairly easy. Although deleting the connection from the connection stack of an user is a little bit complicated.
Let's continue with the sending of a message. Here I defined a structure of the message I get from the Redis server:
{
targets: [], // array of unit ids (can be empty)
data: <mixed> // the real data
}
Sending the data to the sockets looks like:
redis.on('message', function(channel, message) {
message = JSON.parse(message);
// unicast/multicast
if (message.targets.length > 0) {
message.targets.forEach(function(userId) {
if (connIdsForUser[userId] !== undefined) {
connIdsForUser[userId].forEach(function(connId) {
sockets[connId].write(message.data);
});
}
});
// broadcast
} else {
for (var id in sockets) {
if (sockets.hasOwnProperty(id)) {
sockets[id].write(message.data);
}
}
}
});
Since I store the connection stack per user it is quite easy to send the data to all sockets related to a specific user. So what I can do now is unicast (array with one user id), multicast (array with more than one user id) and broadcast (empty array).
It's working well for my use case.
I'm trying to send a post request but It is not sending. I dont get output in the console log of the browser.
My node server.js is running in x.x.x.x:8000 then I connect it with my client.html. x.x.x.x:8000/client.html
Here is my node.js server.
function handler (req, res) {
var filepath = '.' + req.url;
if (filepath == './')
filepath = './client.html';
var extname = path.extname(filepath);
var contentType = 'text/html';
switch(extname){
case '.js':
contentType = 'text/javascript';
break;
case '.css':
contentType = 'text/css';
break;
}
path.exists(filepath, function (exists){
if (exists){
fs.readFile(filepath, function(error, content){
if(error){
res.writeHead(500);
res.end();
}
else{
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
}
});
}
else{
res.writeHead(404);
res.end();
}
});
}
JAVASCRIPT CODE - I'm using ajax call and sending request to COMMAND.php
$.post(
'/var/www/COMMAND.php',
{ cmd: "OUT" },
function( data ){
console.log(data);
});
PHP COMMAND.php - This writes to the the named pipe in linux. When it is done writing it echo success.
<?php
if ($_POST['cmd'] === 'OUT') {
$con = fopen("/tmp/myFIFO", "w");
fwrite($con, "OUT\n");
fclose($con);
echo "SUCCESS";
exit;
}
?>
Why is it not sending any post requests to COMMAND.php? Is there any way for me to call COMMAND.php and execute the commands in it?
Because NodeJS runs JS, not PHP. Also, unlike Apache which has a built-in file handling, in NodeJS, you need to build code or use an existing library to route your urls to files.
As for your question, it's either you:
Setup another server to execute that PHP. Your AJAX is calling to your NodeJS server. You could route that request from NodeJS to your PHP server (Apache or whatever), basically making NodeJS act like a proxy.
Or create code in JavaScript for NodeJS that runs a similar routine as your PHP script, and you won't need PHP or another server anymore.
This is probably a simple mistake. I have a nodejs server running socket.io, I have got everything to work within the server.
However, I want to be able to make a CURL post via PHP to the node server, and have it emit the data. I can make the server receive the request, but when I try to emit the data, I get an error saying that the socket is not defined.
This is obvious in my code. My question is, how do I require socket.io before I setup the server? Hopefully a segment my code will help explain my problem:
var http = require('http')
, url = require('url')
, fs = require('fs')
, server;
server = http.createServer(function(req, res) {
// your normal server code
var path = url.parse(req.url).pathname;
switch (path){
case '/':
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<h1>Hello! Enter</h1>');
res.end();
break;
case '/index.html':
fs.readFile(__dirname + path, function(err, data){
if (err) return send404(res);
res.writeHead(200, {'Content-Type': path == 'json.js' ? 'text/javascript' : 'text/html'})
res.write(data, 'utf8');
res.end();
});
break;
case '/write.html':
fs.readFile(__dirname + path, function(err, data){
if (err) return send404(res);
res.write(data, 'utf8');
res.end();
});
break;
case '/post':
console.log(req.toString());
socket.broadcast.emit('user', {state: 'PHP Post', userid: 'PHP'});
res.writeHead(200);
res.end();
break;
default: send404(res);
}
}),
send404 = function(res){
res.writeHead(404);
res.write('404');
res.end();
};
server.listen(843);
// socket.io
var io = require('socket.io').listen(server);
// Regular socket.io events
how do I require socket.io before I setup the server?
That part is pretty easy; at the top of your file, do something like:
var http = require('http')
, url = require('url')
, fs = require('fs')
, socketIO = require('socket.io') // <-- added line
, server;
and at the bottom:
var io = socketIO.listen(server);
The problem is, in your POST handler, you're using socket.broadcast.emit, but socket isn't defined. Are you trying to send a message to all Socket.IO users? If so, you can use io.sockets.emit; I'd probably do something like this:
var http = require('http')
, url = require('url')
, fs = require('fs')
, server
, io;
...
case '/post':
console.log(req.toString());
io.sockets.emit('user', {state: 'PHP Post', userid: 'PHP'});
res.writeHead(200);
res.end();
break;
...
io = require('socket.io').listen(server);
If you're trying to send data to a single socket, or every socket except for a particular one (which is how you'd normally use socket.broadcast), you'll somehow need to map HTTP requests to Socket.IO sockets; using sessions for this is common.