I'm trying to port this simple PHP script to node.js.
The TV uses RS232 and the command is PON for on; POF for off.
This example successfully turns my TV On:
<?php
$rs232_sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_connect($rs232_sock, '10.0.1.155', '4999');
$rs232_in = pack("H*" ,'02'.bin2hex('PON').'03');
socket_write($rs232_sock, $rs232_in, strlen($rs232_in));
?>
I have this started for NodeJS:
var net = require('net');
var jspack = require('jspack/jspack.js').jspack;
client.connect('4999','10.0.1.155', function(){
console.log('CONNECTED');
// Send the RS232 command
client.write(jspack.Pack("H",'02'+bin2hex(command)+'03'));
}).on('data', function(data) {
// log the response
console.log('DATA: ' + data);
// Close the connection
client.destroy();
});
This is causing:
net.js:618
throw new TypeError('invalid data');
^
TypeError: invalid data
at Socket.write (net.js:618:11)
at Socket.<anonymous> (/Users/paul/Sites/homebridge-globalcache-gc100/test.js:79:10)
at Socket.g (events.js:261:16)
at emitNone (events.js:73:20)
at Socket.emit (events.js:167:7)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1051:10)
You don't need extra libraries to send binary data. Something like this should be sufficient:
var net = require('net');
var PON_MSG = new Buffer('\x02PON\x03', 'binary');
var POF_MSG = new Buffer('\x02POF\x03', 'binary');
client.connect('4999','10.0.1.155', function() {
console.log('CONNECTED');
// Replace `PON_MSG` with `POF_MSG` to do POF instead
client.write(PON_MSG);
}).on('data', function(data) {
console.log('DATA: %j', data);
});
Also be aware that the data event can fire multiple times, so when you can safely end the connection (if the remote side doesn't do so automatically) depends on the protocol (to make sure you have received the entire response).
Related
I am trying to connect a client, ran in a web browser, to a server using websockets.
My client is written in javascript and my server is node.js. I have managed to connect a node.js client with my server but cannot seem to connect my javascript client to the server.
server code:
var webSocketServer = require("websocket").server;
var http = require("http");
var port = 9000;
var server = http.createServer(function (request, response) {
console.log(new Date() + " Received request");
});
server.listen(port, function () {
console.log(new Date() + " listening on port 9000");
});
var socket = new webSocketServer({
httpServer: server
});
socket.on("request", function (request) {
var connection = request.accept(null, request.origin);
connection.on("message", function (message) {
console.log(message.utf8Data);
});
});
client code:
var client = new WebSocket("ws://localhost:9000");
client.onopen = function () {
client.send("Handshake from client");
}
I expected to see the handshake from client output in my command prompt from which I ran my server. This does not happen. In my browsers console I eventually get this error:
WebSocket connection to 'ws://localhost:9000/' failed: WebSocket opening handshake timed out
I tried to console.log my client object and the output was this:
WebSocket {url: "ws://localhost:9000/", readyState: 0, bufferedAmount: 0, onopen: null, onerror: null, …}
So as far as I know the object is being built correctly and pointing at the correct URL but still fails to make the connection.
Here's my setup:
Raspberry Pi 2 (192.168.1.101):
Sensor recording temperature, pressure and humidity.
Python3 script connected to a Raspberry Pi 3, reading sensor data and sending to Pi 3, in JSON format, every 5 seconds.
Raspberry Pi 3 (192.168.1.100):
Node.js server listening for python client on port 8888.
Socket.io listening for web clients on port 3000 (port 3000 and 80 have been opened on my router).
Web server (on port 80) with a website displaying sensor data.
JavaScript connecting to node server, using socket.io, via foobar.ddns.net:3000.
Misc:
I'm using noip.com to have a domain serving my dynamic IP address, my router lets noip know when my public IP changes. I have a URL that looks like foobar.ddns.net.
This setup seems to be working. The Python script is sending data to the node server, which is forwarding it on to any web client connected, which is displayed correctly on the website.
My issue is that the web client disconnects after 1 round of ping/pong between the client and node server.
Here's the chrome console log when connected to the server and receiving data:
The web client connects, receives some data, does a ping/pong with the server, receives some more data, then when it's supposed to ping/pong again it disconnects, then after a while it tries reconnecting and the cycle continues.
And here's the node.js log:
The first New Connection is the Python client (I'm not sure why the IP is the Pi3 address), the rest are the same web client connecting, being disconnected for ping time out, then reconnecting. The client appears to be disconnecting based on on the servers pingInterval + pingTimeout values.
Changing the pingTimeout and pingInterval values just delays the disconnect.
Here's my code:
Python Client:
import json
import socket
import bme280_sensor
import time
import os
class Connection():
def __init__(self, host, port):
self.host = host
self.port = port
def connect(self):
print('Creating socket')
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error as msg:
print('Failed to create socket: %s' % msg)
raise
print('Socket created')
server_address = (self.host, self.port)
print('Connecting to %s:%s' % server_address)
try:
self.sock.connect(server_address)
except socket.error as msg:
print('Failed to connect: %s' % msg)
raise
print('Connected')
def shutdown(self):
print('Shutting down')
self.sock.shutdown(socket.SHUT_RDWR)
self.sock.close()
def measure_temp():
bme_data = bme280_sensor.read_all()
obj = {}
obj['temp'] = round(bme_data[2], 2)
obj['humidity'] = round(bme_data[0], 2)
obj['pressure'] = round(bme_data[1], 2)
return json.dumps(obj)
def sendData(sock):
print('Sending data')
while True:
try:
data = 'data,' + measure_temp()
sock.sendall(data.encode())
except socket.error as msg:
print("Cannot send to server: %s" % msg)
break
time.sleep(5)
connection = Connection('192.168.1.100', 8888)
while True:
try:
connection.connect()
sendData(connection.sock)
connection.shutdown()
break
except socket.error as msg:
print('Connection failed, retrying in 3 seconds.')
time.sleep(3)
print('Done')
Node.js Server:
var net = require('net');
var port = 8888;
var server = net.createServer();
// socket io listens for clients on port 3000
var io = require('socket.io')(3000,{
pingInterval: 10000,
pingTimeout: 5000,
});
// server listens for python client on port 8888
server.listen(port);
console.log('Server started');
// store the last data recorded, so when a socket.io client connects, they can get the last reading instead of waiting for the next one
global.last;
server.on('connection', function(socket){
console.log('New server connection ' + socket.address().address);
// when the server recieves data, send it to the connected socket clients
socket.on('data', function(data){
// strip the first 5 characters from the input string, parse json from the result
var actual = generateJSON(data.toString().substring(5));
// store the dta
global.last = actual;
//send the data
io.sockets.emit('data', actual);
});
});
io.on('connection', function(socket){
console.log('New io connection ' + socket.id);
// if the server has data previously recorded, send it to the new client
if(global.last){
io.emit('data', global.last);
}
socket.on('disconnect', function(reason){
console.log('io disconnect: ' + reason);
});
});
function generateJSON(data){
var dataJSON = JSON.parse(data);
var obj = new Object();
obj.temperature = dataJSON.temp;
obj.humidity = dataJSON.humidity;
obj.pressure = dataJSON.pressure;
obj.datetime = new Date().toString();
return JSON.stringify(obj);
}
Website Javascript:
var socket;
var connected = false;
function connect(){
console.log('connecting...')
if(socket){
socket.destroy()
delete socket;
socket = null;
}
socket = io.connect("http://foobar.ddns.net:3000", {
forceNew: true,
reconnection: true,
reconnectionDelay: 3000,
reconnectionDelayMax: 5000,
reconnectionAttempts: Infinity
});
console.log(socket);
socket.on("data", function(data){
var obj = JSON.parse(data);
console.log(data);
$('#temperature-value').text(obj.temperature);
$('#humidity-value').text(obj.humidity);
$('#pressure-value').text(obj.pressure);
lastUpdate = new Date();
});
socket.on('connect_error', function(error){
console.log('connection error: ' + error);
});
socket.on('connect_timeout', function(){
console.log('connection timeout');
});
socket.on('reconnect', function(){
console.log('reconnect');
});
socket.on('reconnect_attempt', function(){
console.log('reconnect attempt');
});
socket.on('reconnect_failed', function(){
console.log('reconnect_failed');
});
socket.on('reconnect_error', function(){
console.log('reconnect_error');
});
socket.on('reconnecting', function(){
console.log('reconnecting');
});
socket.on('ping', function(){
console.log('ping');
});
socket.on('pong', function(ms){
console.log('pong ' + ms + "ms");
});
socket.on('connect', function(){
console.log('connected to server');
connected = true;
});
socket.on('disconnect', function(reason){
console.log('disconnected from server: ' + reason);
connected = false;
});
}
$(document).ready(function(){
connect();
});
I'm accessing the socket.io.js script with this in my index.html:
<script src="http://foobar.ddns.net:3000/socket.io/socket.io.js"></script>
This is functional but the disconnects are rather annoying, I'd rather the client stays connected. I have a feeling that my node.js server is not setup correctly, but I can't figure out what the issue is. If there's a better way to feed data from the python script > node.js server > web clients then please let me know.
Thanks
I've solved the issue! It had nothing to do with node.js or socket.io.
The issue was on the web page I have displaying the data, I had this method to update a span showing the seconds since the last update:
function updateLastUpdateTimer(){
var seconds = (new Date() - lastUpdate) / 1000;
$('#time-since-last-update').text(formatTime(seconds) + " ago");
$('#last-updated-time').text(lastUpdate);
setInterval(updateLastUpdateTimer, 1000);
}
The issue was setInterval when it should have been setTimeout. I realised that my web page was eating up RAM, which was causing the client socket to hang and not send any data to the server, which was causing the time out!
The setInterval method runs a function every x milliseconds. DO NOT put it in the method you want to call! Call it once instead.
To anyone reading this who has the same issue with ping timeout and transport closed disconnects, check your client!
Background: I have a node.js server running on my localhost (call this Server A); and an external server running node.js at https://example.net:3000 (call this Server B). I do not control or have access to Server B (it is a dashboard site for an IoT device in my home), but I need to connect to is using socket.io and emit a specific message.
I can connect to it easily from a flat javascript file (client-side), but need it running server side (ultimate goal is to make it into something I can call with an HTTP request); and examples such as How to connect two node.js servers with websockets? suggest I should be able to use socket.io-client from node.js with nearly the same code to achieve the same results. But when I run the code from node.js, I cannot connect to the socket.
Below is the code that works successfully in flat javascript file. I know it works because I see 'socket connect' in the console, and I can also test for the the socket emit at the end.
var myemail = "email#gmail.com";
var device_id = '12345';
// Create SocketIO instance, connect
var socket = io.connect('https://example.net:3000');
socket.on('connect', function(){
try {
console.log('socket connect');
socket.emit('configure', {email:myemail, deviceid:device_id});
} catch(e) {
console.log(e);
}
});
socket.emit("/" + device_id, "45678");
...and below is the code I cannot get to work when running from my node.js instance. I'd expect a message 'socket connect' in the command line log and get nothing.
var express=require('express');
var http=require('http');
var app=express();
var server = http.createServer(app);
//Variables
var myemail = "email#gmail.com";
var device_id = '12345';
var io = require('socket.io-client');
var socket = io.connect('https://example.net:3000');
//Connect listener
socket.on('connect', function(){
try {
console.log('socket connect');
socket.emit('configure', {email:myemail, deviceid:device_id});
} catch(e) {
console.log(e);
}
});
socket.emit("/" + device_id, "45678");
Any ideas?
UPDATE
Ran debug utility, results included as linked image below. Key thing I see is that engine.io tries to do an xhr poll, and gets a 503 response back from the server. (Obviously not a true 'temporary error' with the server as again, this all works from running client-side js in chrome).
debugging output image link
Solved this - issue was that the server I was connecting to required use of https, so I needed to add
{secure: true, rejectUnauthorized: false}
after the url to connect to.
Full working example:
const myemail = email#email.com;
const device_id = 12345;
io = require('socket.io-client');
var socket = io.connect('https://server.net:3000',{secure: true, rejectUnauthorized: false});
function doStuff(){
//Listener
socket.on('connect', function(){
try {
console.log('socket connect');
socket.emit('configure', {email:myemail, deviceid:device_id});
} catch(e) {
console.log(e);
}
});
socket.emit("/" + device_id, "003021");
}
doStuff();
I think the line causing the issue is :
var socket = io.connect('https://example.net:3000');
I managed to make a working example using this code :
const myemail = "email#gmail.com";
const device_id = '12345';
var socket = require('socket.io-client')('https://example.net:3000');
socket.on('connect', function(){
try{
console.log('socket connect');
socket.emit('configure', {email:myemail, deviceid:device_id});
}catch(e){ console.log(e); }
});
i tumbled across websocket and http.
I have written below example:
var fs = require('fs');
var server = http.createServer(function(request, response) {
console.log ("HTTP Request created...");
// I am responding something here..
});
server.listen(1234, function() {
console.log((new Date()) + ' Server is listening on port 1234');
});
var WebSocketServer = require('websocket').server;
wsServer = new WebSocketServer({
httpServer: server
});
wsServer.on('request', function(re){
var conn = re.accept(null, re.origin);
console.log ("Accepted New Connection..");
conn.on('message', function(message) {
console.log ('message received' + message.utf8Data);
});
});
I tried in two ways connecting to this server.
1) Through Browser.
2) through node.js application
When I tried reaching this server through browser ex: http:IP:1234,
I get "HTTP Request received.." gets printed, where as when I try with
below code in Node.js, I do not see this message getting printed.
var WebSocket = require('ws')
ws = new WebSocket('ws://IP:1234');
ws.on('open', function() {
console.log ("WebSocket opened..");
ws.send('something');
});
ws.on('message', function(message) {
console.log('received: %s', message.data);
});
When I tried to connect to webserver through
ws = new WebSocket('ws://IP:1234');
why is It not getting through HTTP?. My basic understanding is Websocket is just an upgrade on top of HTTP, in that case, I would assume WebSocket(), in turn connectes through HTTP to the server right?. Or Am i confused?.
Websocket requests don't trigger a request event on the HTTP server instance (for which the function you pass to createServer is a listener).
If you want to watch websocket requests on the HTTP server, listen to upgrade events:
server.on('upgrade', function (req, socket, head) { ... });
I assume it's because http will handle the handshake/upgrade itself that this is done through a different event.
I subscibe to laravel 5 event[channal] update.group and I recive message in console after I trigger event but on client side in browser I don't recive any message. Also after I trigger event I recive message in console and then node server stop working with message:
bash-4.2# node node.js
Listening on Port 3000
Message Recieved: testasdsa
/home/client/public_html/node_modules/ioredis/lib/parsers/javascript.js:216
throw err;
^
SyntaxError: Unexpected token e
at Object.parse (native)
at Redis.<anonymous> (/home/client/public_html/node_modules/node.js:10:20)
at Redis.emit (events.js:110:17)
at Redis.exports.returnReply (/home/client/public_html/node_modules/ioredis/lib/redis/parser.js:79:16)
at ReplyParser.<anonymous> (/home/client/public_html/node_modules/ioredis/lib/redis/parser.js:27:10)
at ReplyParser.emit (events.js:107:17)
at ReplyParser.send_reply (/home/client/public_html/node_modules/ioredis/lib/parsers/javascript.js:281:8)
at ReplyParser.execute (/home/client/public_html/node_modules/ioredis/lib/parsers/javascript.js:210:14)
at Socket.<anonymous> (/home/client/public_html/node_modules/ioredis/lib/redis/event_handler.js:90:22)
at Socket.emit (events.js:107:17)
at readableAddChunk (_stream_readable.js:163:16)
at Socket.Readable.push (_stream_readable.js:126:10)
at TCP.onread (net.js:538:20)
Here is my node.js file:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
redis.subscribe('update.group', function(err, count) {
});
redis.on('message', function(channel, message) {
console.log('Message Recieved: ' + message);
message = JSON.parse(message);
io.sockets.emit(channel + ':' + message.event, message.data);
});
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
And for client side:
var socket = io.connect('127.0.0.1:3000');
socket.on("update.group", function(message){
// increase the power everytime we load test route
console.log(message);
});
Anyone can find what is problem?
It's pretty obvious from your debug output: testasdsa is not valid JSON, so you cannot parse it as such. You will need to change the code that publishes the messages so that it's properly JSON encoded.
Since it looks like you're expecting event and data properties on an object, the publisher would need to be writing something like { "event": "foo", "data": "testasdsa" }.
To fix the problem of browser clients not getting the events, you need to change this in your server script:
io.sockets.emit(channel + ':' + message.event, message.data);
to:
io.sockets.emit(message.event, message.data);
Then just make sure the publisher sends out something like { "event": "update.group", "data": "somedata" }