How to connect node.js with ada using sockets? - javascript

I'm trying to connect my Ada application with node.js server using sockets.
I've already managed to send data from Ada to node.js but still have problems with receiving datas.
Here's my Ada task:
task body Send_Task is
use Ada.Numerics.Float_Random;
Next : Ada.Real_Time.Time;
Period : constant Ada.Real_Time.Time_Span := Ada.Real_Time.Milliseconds(5000);
Interval : constant Ada.Real_Time.Time_Span := Ada.Real_Time.Milliseconds(30);
Gen : Generator;
Address : Sock_Addr_Type;
Socket : Socket_Type;
Channel : Stream_Access;
begin
Reset(Gen);
Next := Ada.Real_Time.Clock + Interval;
Address.Addr := Addresses (Get_Host_By_Name (Host_Name), 1);
Address.Port := 9000;
Create_Socket (Socket);
Set_Socket_Option (Socket, Socket_Level, (Reuse_Address, True));
Connect_Socket (Socket, Address);
loop
delay until Next;
Channel := Stream (Socket);
String'Output(Channel, Message'Img);
Put_Line("Input " & String'Input(Channel));
Next := Next + Period;
end loop;
exception
when E:others => Put_Line("Error: Task Errors");
Put_Line(Exception_Name (E) & ": " & Exception_Message (E));
end Send_Task;
this is my node.js server:
require('net').createServer(function (socket) {
console.log("connected");
socket.on('data', function (data) {
console.log(data.toString());
});
socket.on('connection', function(){
console.log("test2");
socket.write("900.0");
});
console.log("test1");
socket.write("900.0");
}).listen(9000);
My console output for node.js server:
connected
test1
0.00 //value of Message'Img
There's no output in Ada's task. It seems like loop in task has stopped and waits for message from node.js. Without the line Put_Line("Input " & String'Input(Channel)); everything works fine (except of course getting messages from node.js).
Thanks for help!

Your problem is that your server isn't sending correctly formatted data. 13.13.2(26/3) in the reference manual specifies that String'Input first reads the bounds of the string, and then its contents.
You probably want to send using String'Write and receive using Character'Read (unless you have a well-defined message length).

Related

Socket.io client disconnects due to ping timeout / transport closed

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!

node js read specific message from tcp socket net.createServer

var net = require('net');
var HOST = '0.0.0.0';
var PORT = 5000;
// Create a server instance, and chain the listen function to it
// The function passed to net.createServer() becomes the event handler for the 'connection' event
// The sock object the callback function receives UNIQUE for each connection
net.createServer(function(sock) {
// We have a connection - a socket object is assigned to the connection automatically
console.log('CONNECTED: ' + sock.remoteAddress +':'+ sock.remotePort);
// Add a 'data' event handler to this instance of socket
sock.on('data', function(data) {
console.log('DATA ' + sock.remoteAddress + ': ' + data);
// Write the data back to the socket, the client will receive it as data from the server
if (data === "exit") {
console.log('exit message received !')
}
});
// Add a 'close' event handler to this instance of socket
sock.on('close', function(data) {
console.log('CLOSED: ' + sock.remoteAddress +' '+ sock.remotePort);
});
}).listen(PORT, HOST);
console.log('Server listening on ' + HOST +':'+ PORT);
No matter what I try, I cannot get:
if (data === "exit") {
console.log('exit message received !')
}
working, it's always false.
I'm connecting via telnet and sending "exit", the server should then go into the "if" loop and say "exit message received". This never happens, can someone shed some light ? thanks
That's because data is not a string, if you try to compare with === you will get false because types don't match.
To solve it you should compare the data object with a simple == or use socket.setEncoding('utf8') previous to binding the data event.
https://nodejs.org/api/net.html#net_event_data
var net = require('net');
var HOST = '0.0.0.0';
var PORT = 5000;
net.createServer(function(sock) {
console.log('CONNECTED:',sock.remoteAddress,':',sock.remotePort);
sock.setEncoding("utf8"); //set data encoding (either 'ascii', 'utf8', or 'base64')
sock.on('data', function(data) {
console.log('DATA',sock.remoteAddress,': ',data,typeof data,"===",typeof "exit");
if(data === "exit") console.log('exit message received !');
});
}).listen(PORT, HOST, function() {
console.log("server accepting connections");
});
Note.
If the data received is going to be big you should concatenate and handle the message comparison at the end of it. Check other questions to handle those cases:
Node.js net library: getting complete data from 'data' event
I am aware this is quite an old post, when I tried to implement the code in the answer to this question I came across the same problem regardless of having used "==" or utf8 encoding. The issue for me turned out to be that the client I was using was appending a '\n' character to the end of the exit message thus causing the string comparison to fail on the server. Perhaps this is not an issue with telnet and the like but this was the case with netcat. Hopefully this sheds some light for anyone else who comes across this post and has the same problem that I did.

How can i stop a child process by its reference variable in node.js on Debian?

I have a Node.js server set up and i am trying to stop a process which are started when some conditions are met.
Here is my server script:
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var fs = require('fs');
var sys = require('sys')
var exec = require('child_process').exec;
var child;
var musicDirectory = __dirname + '/music/';
var musicFiles = [];
fetchMusic();
server.listen(80);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/views/index.html');
});
io.on('connection', function (socket) {
console.log("We have a new visitor.");
socket.emit('music', {files: musicFiles} );
socket.on('File to play', function (fileToPlay) {
exec("ls " + musicDirectory, function (error, stdout, stderr) {
var filesInMusicDirectory = stdout.split("\n");
for (var i = 0; i < filesInMusicDirectory.length; i++) {
if (filesInMusicDirectory[i] == fileToPlay) {
if (typeof child !== "undefined") {
child.kill();
console.log("Killing process");
}
child = exec("mplayer " + "'" + musicDirectory + fileToPlay + "'");
console.log(fileToPlay + " is in directory.");
break;
}
}
if (error !== null) {
console.log('exec error: ' + error);
}
});
});
});
function fetchMusic()
{
fs.readdir(musicDirectory, function(err,data) {
for (var i = 0; i < data.length; i++) {
musicFiles.push(data[i]);
};
});
}
As you can se i have declared a variable named child on line 8 which i am using like this: child = exec("mplayer " + "'" + musicDirectory + fileToPlay + "'");.
Now, all i want to do, is to stop this process when some conditions are met, so i am trying to to this with child.kill().
The functionality of my little experiment works up to the point where i want to kill the child process. It does not do that, the child.kill() function seems to be failing, and probably because i have missunderstood something.
Could anyone try help me understand why this process wont end. I am reading the documentation as i am writing this, but there are a lot of things to go through.
My question is: Why does not the kill() function stop the process with my code, and what can i do to achieve what i want?
Update
Ok i have fiddled around a bit. I added tree-kill since the child.kill() functionality did not work, and tried to use it with the extracted Pid from the child process which i know is not null or undefined like this: kill = require('tree-kill') and kill(child.pid).
Now the process, or rather, the music stops when the same conditions are met but it it doesnt go thorugh with the rest, playing another song. Instead there are outputted the following in the server console:
Baby (metal cover by Leo Moracchioli).mp3 fed to mPlayer
undefined
STDOUT: MPlayer2 2.0-728-g2c378c7-4+b1 (C) 2000-2012 MPlayer Team
Cannot open file '/root/.mplayer/input.conf': No such file or directory
Failed to open /root/.mplayer/input.conf.
Cannot open file '/etc/mplayer/input.conf': No such file or directory
Failed to open /etc/mplayer/input.conf.
Playing /home/danlevi/Documents/NodeServer/music/Bad Blood (metal cover by Leo Moracchioli).mp3.
Detected file format: MP2/3 (MPEG audio layer 2/3) (libavformat)
[mp3 # 0xb62803a0]max_analyze_duration 5000000 reached
[lavf] stream 0: audio (mp3), -aid 0
Clip info:
major_brand: dash
minor_version: 0
compatible_brands: iso6mp41
creation_time: 2015-09-29 08:04:29
encoder: Lavf54.20.4
Load subtitles in /home/danlevi/Documents/NodeServer/music/
Selected audio codec: MPEG 1.0/2.0/2.5 layers I, II, III [mpg123]
AUDIO: 44100 Hz, 2 ch, s16le, 128.0 kbit/9.07% (ratio: 16000->176400)
AO: [pulse] 44100Hz 2ch s16le (2 bytes per sample)
Video: no video
Starting playback...
MPlayer interrupted by signal 15 in module: unknown
Exiting... (Quit)
A: 9.1 (09.0) of 281.8 (04:41.7) 0.2%
A: 9.1 (09.0) of 281.8 (04:41.7) 0.2%
So i think i have made som progress in the right direction, now i know that i have the correct process, and mPlayer is indeed interrupted, but the new song does not play.
The reason why this dont work is because the correct kill signal is not sent.
The SIGTERM signal which is the default signal is a generic signal used to cause program termination. Unlike SIGQUIT, this signal can be blocked, handled, and ignored. It is the normal way to politely ask a program to terminate.
Since the application should kill the process immediately we use SIGQUIT.
It is also important to adjust the timeout and buffer for the child process so that it finishes.

How to detect client disconnection from node.js server

I am new to node.js. How to detect client is disconnected from node.js server .
Here is my code:
var net = require('net');
var http = require('http');
var host = '192.168.1.77';
var port = 12345;//
var server = net.createServer(function (stream) {
stream.setEncoding('utf8');
stream.on('data', function (data) {
var comm = JSON.parse(data);
if (comm.action == "Join_Request" && comm.gameId =="game1") // join request getting from client
{
var reply0 = new Object();
reply0.message = "WaitRoom";
stream.write(JSON.stringify(reply0) + "\0");
}
});
stream.on('disconnect', function() {
});
stream.on('close', function () {
console.log("Close");
});
stream.on('error', function () {
console.log("Error");
});
});
server.listen(port,host);
How to know client side internet disconnection.
The best way to detect "dead sockets" is to send periodic application-level ping/keepalive messages. What that message looks like depends on the protocol you're using for communicating over the socket. Then it's just a matter of using a timer or other means of checking if you've received a "ping response" within a certain period of time after you sent the ping/keepalive message to the client.
On a semi-related note, it looks like you're using JSON messages for communication, but you're assuming a complete JSON string on every data event which is a bad assumption. Try using a delimiter (a newline is pretty common for something like this, and it makes debugging the communication more human-readable) instead.
Here is a simple example of how to achieve this:
var PING_TIMEOUT = 5000, // how long to wait for client to respond
WAIT_TIMEOUT = 5000; // duration of "silence" from client until a ping is sent
var server = net.createServer(function(stream) {
stream.setEncoding('utf8');
var buffer = '',
pingTimeout,
waitTimeout;
function send(obj) {
stream.write(JSON.stringify(obj) + '\n');
}
stream.on('data', function(data) {
// stop our timers if we've gotten any kind of data
// from the client, whether it's a ping response or
// not, we know their connection is still good.
clearTimeout(waitTimeout);
clearTimeout(pingTimeout);
buffer += data;
var idx;
// because `data` can be a chunk of any size, we could
// have multiple messages in our buffer, so we check
// for that here ...
while (~(idx = buffer.indexOf('\n'))) {
try {
var comm = JSON.parse(buffer.substring(0, idx));
// join request getting from client
if (comm.action === "Join_Request" && comm.gameId === "game1") {
send({ message: 'WaitRoom' });
}
} catch (ex) {
// some error occurred, probably from trying to parse invalid JSON
}
// update our buffer
buffer = buffer.substring(idx + 1);
}
// we wait for more data, if we don't see anything in
// WAIT_TIMEOUT milliseconds, we send a ping message
waitTimeout = setTimeout(function() {
send({ message: 'Ping' });
// we sent a ping, now we wait for a ping response
pingTimeout = setTimeout(function() {
// if we've gotten here, we are assuming the
// connection is dead because the client did not
// at least respond to our ping message
stream.destroy(); // or stream.end();
}, PING_TIMEOUT);
}, WAIT_TIMEOUT);
});
// other event handlers and logic ...
});
You could also just have one interval instead of two timers that checks a "last data received" timestamp against the current timestamp and if it exceeds some length of time and we have sent a ping message recently, then you assume the socket/connection is dead. You could also instead send more than one ping message and if after n ping messages are sent and no response is received, close the connection at that point (this is basically what OpenSSH does).
There are many ways to go about it. However you may also think about doing the same on the client side, so that you know the server didn't lose its connection either.

Chrome packaged app UDP sockets not working

I'm trying to get UDP sockets working for a packaged app using Chrome Canary (currently version 25). I am pretty confused by the fact the UDP example here conflicts with the reference documentation here.
The official example uses this line:
chrome.socket.create('udp', '127.0.0.1', 1337, { onEvent: handleDataEvent }, ...
In Canary using this line results in the error:
Uncaught Error: Invocation of form socket.create(string, string,
integer, object, function) doesn't match definition
socket.create(string type, optional object options, function callback)
Not surprising since that matches the documented form of the function. (I guess the example is out of date?) OK, so I try this...
chrome.socket.create('udp', { onEvent: handleDataEvent }, ...
Canary complains:
Uncaught Error: Invalid value for argument 2. Property 'onEvent':
Unexpected property.
Now I'm confused, especially since this parameter is undocumented in the reference. So I just go with this:
chrome.socket.create('udp', {}, ...
Now it creates OK, but the following call to connect...
chrome.socket.connect(socketId, function(result) ...
...fails with this:
Uncaught Error: Invocation of form socket.connect(integer, function)
doesn't match definition socket.connect(integer socketId, string
hostname, integer port, function callback)
...which is not surprising, since now my code doesn't mention a host or port anywhere, so I guess it needs to be in connect. So I change it to the form:
chrome.socket.connect(socketId, address, port, function (result) ...
At last I can connect and write to the socket OK. But this doesn't cover reading.
Can someone show me a working example based on UDP that can send & receive, so I can work from that?
How do I receive data since the example's onEvent handler does not work? How do I ensure I receive any data on-demand as soon as it arrives without blocking?
The Network Communications doc is not up-to-date. See the latest API doc: https://developer.chrome.com/trunk/apps/socket.html. But the doc doesn't state everything clearly.
I looked into Chromium source code and found some useful comments here: https://code.google.com/searchframe#OAMlx_jo-ck/src/net/udp/udp_socket.h&q=file:(%5E%7C/)net/udp/udp_socket%5C.h$&exact_package=chromium
// Client form:
// In this case, we're connecting to a specific server, so the client will
// usually use:
// Connect(address) // Connect to a UDP server
// Read/Write // Reads/Writes all go to a single destination
//
// Server form:
// In this case, we want to read/write to many clients which are connecting
// to this server. First the server 'binds' to an addres, then we read from
// clients and write responses to them.
// Example:
// Bind(address/port) // Binds to port for reading from clients
// RecvFrom/SendTo // Each read can come from a different client
// // Writes need to be directed to a specific
// // address.
For the server UDP socket, call chrome.socket.bind and chrome.socket.recvFrom/chrome.socket.sendTo to interact with clients. For the client UDP socket, call chrome.socket.connect and chrome.socket.read/chrome.socket.write to interact with the server.
Here's an example:
var serverSocket;
var clientSocket;
// From https://developer.chrome.com/trunk/apps/app_hardware.html
var str2ab=function(str) {
var buf=new ArrayBuffer(str.length);
var bufView=new Uint8Array(buf);
for (var i=0; i<str.length; i++) {
bufView[i]=str.charCodeAt(i);
}
return buf;
}
// From https://developer.chrome.com/trunk/apps/app_hardware.html
var ab2str=function(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
};
// Server
chrome.socket.create('udp', null, function(createInfo){
serverSocket = createInfo.socketId;
chrome.socket.bind(serverSocket, '127.0.0.1', 1345, function(result){
console.log('chrome.socket.bind: result = ' + result.toString());
});
function read()
{
chrome.socket.recvFrom(serverSocket, 1024, function(recvFromInfo){
console.log('Server: recvFromInfo: ', recvFromInfo, 'Message: ',
ab2str(recvFromInfo.data));
if(recvFromInfo.resultCode >= 0)
{
chrome.socket.sendTo(serverSocket,
str2ab('Received message from client ' + recvFromInfo.address +
':' + recvFromInfo.port.toString() + ': ' +
ab2str(recvFromInfo.data)),
recvFromInfo.address, recvFromInfo.port, function(){});
read();
}
else
console.error('Server read error!');
});
}
read();
});
// A client
chrome.socket.create('udp', null, function(createInfo){
clientSocket = createInfo.socketId;
chrome.socket.connect(clientSocket, '127.0.0.1', 1345, function(result){
console.log('chrome.socket.connect: result = ' + result.toString());
});
chrome.socket.write(clientSocket, str2ab('Hello server!'), function(writeInfo){
console.log('writeInfo: ' + writeInfo.bytesWritten +
'byte(s) written.');
});
chrome.socket.read(clientSocket, 1024, function(readInfo){
console.log('Client: received response: ' + ab2str(readInfo.data), readInfo);
});
});

Categories