I am trying to build a node server which as a middleman for my website. Several libraries are used.
Axios, I use axios to post requests to API and get the data from database
Socket.io, I use socket.io for recording who login and broadcast the message to every user if needed.
Express, I use it to host my React web app.
For the web app, I use componentDidMount and Axios to fetch data when the page is started and pressed the login button respectively. However, not every time the node server response, I will say its freezed. Sometime I press "Esc", and it will response the message back. How can I make sure it returns every time? Thanks a lot!
Partial Code from node js:
server.js
#for access DB
const DBhttp = require('http');
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(morgan('common', { stream: serverLogStream}));
app.use('/api/login', loginRouter);
app.use('/api', router);
let DBserver;
DBserver = DBhttp.createServer(app)
#Express for host app
var AppServer;
var http;
var webApp = express();
webApp.use(express.static(path.join(__dirname, 'build')));
webApp.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
AppServer= http.createServer(options, webApp);
#socket.io commumicate between app
const socketIO = require("socket.io");
var io = socketIO.listen(server);
var clients = {};
io.sockets.on('connection', function (socket) {
#do the communication
}
React
react_index.js
initializeSession(this.state.loginName); #connect socket
this.setState({isLogin:true});
axios.post(SERVER_NAME + 'api/afterLogin')
.then((res) => {
this.setState({
full_name : res.data,
})
return Promise.resolve('Success')
})
You can add one more client right on your server to connect it to the same channel and see all the responses.
You can write the simple index.html with alike code:
<!doctype html>
<body>
<ul id="messages"></ul>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
<script>
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
var socket = io.connect({'YOUR PATH TO SOCKET SERVER'});
socket.on('connect', () => {
console.log('socket.on connect');
});
socket.on('message', function (msg) {
$('#messages').append($('<li>').text(JSON.stringify(msg)));
});
socket.on('update', function (msg) {
$('#messages').append($('<li>').text(JSON.stringify(msg)));
});
socket.on('disconnect', () => {
console.log('socket.on disconnect');
})
</script>
</body>
On editing it as you need, you can enable it like this:
app.get('/socketIo', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
Now you can see all the responses, which your server sends to the address {YOUR PATH TO SERVER}/socketIo
Also it would be beneficial to add console.log, to get the information about the clients
io.clients((error, clients) => {
if (error) throw error;
console.log('clients ', clients);
});
This way you'll know whether your client is working
Related
I've a node api(POST)in which the sensor keep on pushing the data to the MongoDB. Now I've an api(GET) which fetches the data from the database and displays on the dashboard. To get the continuous stream of data, I want to use SOCKET.IO module. But the problem is, how could I get the recently saved record from the db and show that on dashboard without reloading the page. Please have a look at my code.
SERVER.JS
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
// and manything like router, middleware, etc...
io.on('connection', function(socket){
console.log('a user connected');
});
http.listen(3000, function(){
console.log('listening on port:3000');
});
ROUTES FILE
var router = require("express").Router();
router.post("/upload/device/data/:tempId", TemplateController.AddDeviceData); //To insert the data to the DB
router.get("/view/template/device/logs/:tempUniqID", TemplateController.deviceLogs); //To get the data from DB
TEMPLATE CONTROLLER FILE
module.exports={
AddDeviceData:async function(req, res){ //Controller to post data
let err, deviceLog;
[err, deviceLog]=await
to(TemplateService.AddDeviceLogs(req.params.tempId, req.body));
if(err) return res.serverError(err.message);
if(deviceLog&&deviceLog!==false){
return res.ok(deviceLog);
}else{
res.badRequest("Sorry cannot add Device log data");
}
},
deviceLogs: async function(req, res){ //Controller to fetch data
let err, logs;
let deviceId = req.query.device;
[err, logs]=await to(TemplateService.displayLogs(req.params.tempUniqID, deviceId));
if(err) return res.serverError(err.message);
if(logs&&logs!==false){
return res.ok(logs);
}else{
res.badRequest("Sorry cannot add Device log data");
}
}
}
TEMPLATE SERVICE FILE
module.exports={
//Service to post data
AddDeviceLogs:async function(templateId, payload){
let err, deviceData;
payload.template=templateId;
const myCollection=templateId;
[err, deviceData]=await to(mongoose.connection.db.collection(myCollection).insert(payload));
if(err) TE(err.message, true);
socket.emit('data', deviceData);
return (deviceData)? deviceData.result:false;
},
//Service to get data
displayLogs:async function(tempUniqID, deviceID){
let err, respData;
var Query = (deviceID)? {"template": tempUniqID, "deviceId": deviceID}:{template: tempUniqID};
[err, respData]=await to(mongoose.connection.db.collection(tempUniqID).find(Query).sort({_id: -1}).limit(20).toArray())
if(err) {TE(err, true);}
return (respData)? respData:false;
}
}
Now I want to get most recently stored data in GET api using socket without reloading the page or without executing the GET route-api. I'm not getting which service I should use server socket-emit event in and how.
You can run node and your socket io in the same port, the following example used express and socket.io. I also created sensor code to imagine this solution:
You should use your route file like this:
ROUTES FILE
var router = require("express").Router();
router.post("/upload/device/data/:tempId", TemplateController.AddDeviceData);
router.get("/", function (req, res, next) {
res.sendFile('C:/Users/user/Desktop/data.html');
})
In MVC you will have a root file declare all works, you should delare your socket here.
Because in your codebase, every time you call AddDeviceLogs function, it will re-declare websocket, and your socket client in html file will disconnect, that's why it only work for the first time.
Then you should declare it global, for example:
server.js
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
// and manything like router, middleware, etc...
io.on('connection', function(socket){
console.log('a user connected');
});
http.listen(3000, function(){
console.log('listening on port:3000');
});
TEMPLATE SERVICE FILE
module.exports={
AddDeviceLogs:async function(templateId, payload){
let err, deviceData;
payload.template=templateId;
const myCollection=templateId;
io.emit('data', payload) // emit to all client
[err, deviceData]=await to(mongoose.connection.db.collection(myCollection).insert(payload));
if(err) TE(err.message, true);
return (deviceData)? deviceData.result:false;
}
}
data.html
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
</head>
<body>
<script>
var socket = io.connect("http://localhost:3000/",{"forceNew": true});
socket.on('data', function(data){
if (data) {
$('#deviceid').text(data.deviceId);
$('#heat').text(data.heat);
$('#humidity').text(data.humidity);
}
});
</script>
<h4>Welcome to socket.io testing program!</h4>
<div id="deviceid"></div>
<div id="heat"></div>
<div id="humidity"></div>
</body>
</html>
I am trying to show a component when someone goes to /videocall on the React side of my application.
<BrowserRouter>
<Route path="/videocall" component={VideoCall} />
</BrowserRouter>
and here's how someone can go to that route through a button click from a different component.
<Link to='/videocall'>
Go to Video Call
</Link>
And up until here it's working fine. I can see my new VideoCall component.
But I also want this route handler on the Node/Express side of my application so I can start a Socket.io server.
Since React and Express rendering are different so express doesn't know how to handle /videocall.
I am using it like below.
const socketIO = require('socket.io');
module.exports = (app, server) => {
app.get('/api/videocall', (req, res) => {
const io = socketIO(server);
io.on('connection', (socket) => {
console.log('User connected');
socket.on('message', (msg) => {
console.log(msg);
});
socket.on('disconnect', () => {
console.log('User disconnected');
});
});
});
}
And I am using a wildcard route match that will render the default index.html file like so:
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
And it's working fine. When I go to /videocall from the URL I get routed to my VideoCall component.
But the client is only getting connected to the socket server when I go to /api/videocall and not when I go to /videocall.
How do I resolve that since I want the client to get connected to the socket server when he goes to VideoCall component from a different component on the front end like so:
<Link to='/videocall'>
Go to Video Call
</Link>
So, I finally managed to make it work.
Here's how I did it.
I wrote my socket code in my index.js file and not in any route handler.
const socket = require('socket.io');
const PORT = process.env.PORT || 5000;
const server = app.listen(PORT);
const io = socket(server);
io.on('connection', (socket) => {
console.log('User connected');
socket.on('message', (msg) => {
console.log(msg);
});
socket.on('disconnect', () => {
console.log('User disconnected');
});
});
And in my VideoCall component I wrote my connect method inside componentDidMount lifecycle method. So now I am only connecting when I go that /videocall route.
import io from 'socket.io-client';
var socket;
class VideoCall extends Component {
componentDidMount() {
socket = io('http://localhost:5000');
}
}
I'm trying to build an endpoint that will receive a request, emit the request data to a WebSocket client, wait for an event, then send back the response using express + socketio. This question is similar to it: Wait for socketio event inside express route
1) Receive request at http://localhost:3000/endpoint
2) Emit the event to web sockets as 'req'
3) Wait for 'res' event from ws
4) Send the received events details as the response of express.
Here is how I'm implemented:
server.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
var socket;
io.on('connection', function (s) {
socket = s;
});
http.listen(3000);
app.get('/endpoint', function (req, res) {
console.log('new request')
io.emit('req', { data: 'hello' });
socket.on('res', function (data) {
res.status(200).json(data);
});
});
index.html
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
socket.on('req', (data) => {
console.log(data)
socket.emit('res', data);
});
</script>
The script works fine for the first request on /endpoint. But if i hit the url again, it says
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
Please note that:
socket.on('res', function (data) {
res.status(200).json(data);
});
Is being called each time a socket is sending a response, thus showing the above error. You should unbind the listener inside the callback function.
Keep an array of express responses and set an id to each request. So it can be used later and delete if needed.
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var timeout = require('connect-timeout');
var uuid = require('uuidv4');
var _ = require('lodash');
app.use(timeout('10s'));
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
let responses = []
io.on('connection', (socket) => {
socket.on('res', (e) => {
var obj = _.find(responses, r => r.id === e.id);
obj.res.send(e)
_.remove(responses, r => r.id === e.id);
})
})
app.get('/endpoint', (req, res) => {
const id = uuid()
io.emit('req', { id, ip: req.ip, header: req.headers, method: req.method });
responses.push({ id, res })
});
http.listen(3000);
You're trying to do two different async tasks for the same data.
First, take your socket.on('res'...) out of the app.get().
Send back res.status(200) immediately with express to say you received the request and it is processing. Then send the socket message to the client using socket.io when it's complete. You'll want to save the connected users socket client ID and use io.to(socketId).emit(...data...) to do this
the other option is what I always do (assuming it's not a crazy large payload of data you're sending) Just use socket.io for the whole process.
client
function makeRequest () {
socket.on('data-complete--error', function ( error ) {
// ... message to user :(
// also remove these handlers when finished
socket.off('data-complete--error');
socket.off('data-complete--success');
});
socket.on('data-complete--success', function ( data ) {
// ... message to user :)
// ... handle data
// also remove these handlers when finished
socket.off('data-complete--error');
socket.off('data-complete--success');
});
socket.emit('request-data');
}
makeRequest();
server
move your stuff out and handle without using express at all
Tried different methods, but the data is sent to a maximum of one or two clients. How to send data to all the clients connected to the server ? What am I doing wrong?
Server.js:
var PORT = 3000;
var options = {
// 'log level': 0
};
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server, options);
server.listen(PORT);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/attantions/templates/.default/template.php');
});
io.sockets.on('connection', function (client) {
client.on('attantion', function (data) {
try {
// Tried so
io.sockets.volatile.emit('attantion', data);
// And tried so
io.sockets.emit('attantion', data);
client.emit('attantion', data);
client.broadcast.emit('attantion', data );
} catch (e) {
console.log(e);
client.disconnect();
}
});
});
Client.js:
socket.emit("attantion", data);
socket.on('attantion', function (data) {
pushData(data);
});
See this post for different options for socket.io messages
Send response to all clients except sender (Socket.io)
io.sockets.on('connection', function (client) {
client.on('attantion', function (data) {
//client.emit('attantion', data ); // This will send it to only the client
//client.broadcast.emit('attantion', data); // This will send it to everyone but this client
io.emit('attantion', data); // This will send it to all attached sockets.
});
});
Edit
I wonder if this post can help you?
Socket.io - Cannot load file
I was curious how sending the php file to the client through node.js works? are you using another framework?
Could you show more of what your client code looks like? loading the lib and the instantiation of the socket.
I know this question is kind of awkward, but the problem comes from Samsung TV 2010 / 2011 SmartTV (and blue ray player; of course 2012 emulator working fine). I ported the simple chatting examples come from the source and package to SmartTV app. Both of them fall back to JSONP polling, but from SmartTV app only could emit / push to server once. Receiving the message from server could be multiple times without any problem. After looking for the answer in Samsung D forum (of course nothing there), I think the fastest way to work around this issue is to deploy an Express server, taking the post data and JSON.parse, then emit Socket.io / Sockjs internally inside the server itself.
Could anybody show me an easy sample code so I could start from there? Thanks a lot.
I quickly make code, but seems it doesn't work:
lib/server.js
var express = require('express')
, app = express.createServer()
, io = require('socket.io').listen(app);
app.listen(80);
app.use(express.bodyParser());
app.get('/', function (req, res) {
res.sendfile('/var/www/mpgs_lite_v3/index.html');
});
app.post('/', function(req, res){
console.log(req.body);
io.sockets.emit('my other event', req.body);
res.redirect('back');
});
io.sockets.on('connection', function (socket) {
//socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
index.html
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
</head>
<body>
<form method="post" action="/">
<input type="hidden" name="_method" value="put" />
<input type="text" name="user[name]" />
<input type="text" name="user[email]" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
'my other event' seems not receive anything.
UPDATE: I updated the example for you to make it more complete. I didn't have an app.listen before, and here is also a client side script which shows that it, indeed, works fine:
<!doctype html>
<html>
<head>
<script src="//www.google.com/jsapi"></script>
<script src="/socket.io/socket.io.js"></script>
<script>google.load("jquery", "1.7.1")</script>
<script>
var socket = io.connect("localhost", {port: 3000});
socket.on("foo", function(message) { console.log("foo: ", message) });
$(function() {
$("button").click(function() {
$.post("/foo", { message: $("input").val() });
});
});
</script>
</head>
<body>
<input type=text>A message</input>
<button>Click me!</button>
</body>
</html>
And the server, now with an app.listen directive:
var express = require("express"),
app = express.createServer(),
io = require("socket.io").listen(app)
index = require("fs").readFileSync(__dirname + "/index.html", "utf8");
app.use(express.bodyParser());
app.get("/", function(req, res, next) {
res.send(index);
});
app.post("/foo", function(req, res, next) {
io.sockets.emit("foo", req.body);
res.send({});
});
app.listen(3000);
Usage:
node app.js
Navigate to http://localhost:3000/ and click the button. Check your console for output.
Based on SockJS express example server.js could look like:
var express = require('express');
var sockjs = require('sockjs');
// 1. Echo sockjs server
var sockjs_opts = {sockjs_url: "http://cdn.sockjs.org/sockjs-0.2.min.js"};
var sockjs_echo = sockjs.createServer(sockjs_opts);
connections = {};
sockjs_echo.on('connection', function(conn) {
console.log(conn.id);
connections[conn.id] = conn
conn.on('close', function() {
delete connections[conn.id];
});
// Echo.
conn.on('data', function(message) {
conn.write(message);
});
});
// 2. Express server
var app = express.createServer();
sockjs_echo.installHandlers(app, {prefix:'/echo'});
console.log(' [*] Listening on 0.0.0.0:9999' );
app.listen(9999, '0.0.0.0');
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
app.post("/send", function(req, res, next) {
for(var id in connections) {
connections[id].write('received POST');
}
res.send({});
});
To test open browser at localhost:9999 and run:
curl localhost:9999/send -X POST
just remove this comment
//socket.emit('news', { hello: 'world' });
to
socket.emit('news', { hello: 'world' });
it will work because its emiting data through news and you are listening using my other event instead of 'news' or you can do just listen using 'my other event'
I don't know if this would help, but you can make an emit abstraction on the client based on your browser and then make a separate get function on the server that will handle the request the same way as the socket.on callback. In order to know where to send the information I suggest you use some key that you can store in a hash table in the server and local storage on the client.
For the client:
var emit = function(event, options) {
if ("WebSocket" in window) {
socket.emit(event, options);
console.log("emited via WebSocket");
} else {
$.post("http://localhost/emit/" + event, options);
console.log("emited via AJAX");
}
}
emit("echo", {
key: localStorage.getItem("key"),
data: {
hello: "world"
}
});
socket.on("response", function(data) {
console.log(data.hello); //will print "world"
});
For the server:
var sockets = {};
var echo_handler = function(a) {
var socket = sockets[a.key];
var data = a.data;
socket.emit("response", data);
}
app.post("/emit/:event", function(req, res) {
var event = req.params.event;
switch (event) {
case "echo":
var a = {
key: req.param("key"),
data: req.param("data")
}
echo_handler(a);
break;
}
});
io.sockets.on("connection", function(socket) {
socket.on("connect", function(data) {
sockets[data.key] = socket;
});
socket.on("echo", echo_handler);
});
Another way to do this will be to switch to Sockjs and use their patch.
If someone have better solution for Socket.IO it will be appreciated, because I'm already deep into the project and it's too late to switch Socket.IO for Sockjs, and this solution is not to my liking :( .