Node.js tcp sockets and linked Docker containers - node.js

I have two node apps in two Docker containers.
Main web app
A profile service using sockets over tcp
I link the Profile Service container to the Main Web App container using this option when running the Docker container
--link srv-profile:srv_profile
And it seems to be linking it just fine. I can say this because I am able to see the following in my Main web app container:
root#8d6247a82b6d:/app# echo $SRV_PROFILE_PORT
tcp://10.1.0.8:4000
root#8d6247a82b6d:/app#
The problem I am facing is that my Main web app container is not able to establish a connection with my Profile Service container. Here is the output when I start my main web app:
root#8d6247a82b6d:/app# nodemon server.js
3 Nov 01:22:43 - [nodemon] v1.2.1
3 Nov 01:22:43 - [nodemon] to restart at any time, enter `rs`
3 Nov 01:22:43 - [nodemon] watching: *.*
3 Nov 01:22:43 - [nodemon] starting `node server.js`
web.main listening on port 4000...
Connection closed
Here is my code for both Profile Service and Main Web App
srv-profile\server.js
var net = require('net');
var HOST = '127.0.0.1';
var PORT = 4000;
// 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 + ':');
console.log(data);
console.log();
// Write the data back to the socket, the client will receive it as data from the server
sock.write('You said "' + data + '"');
});
// 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);
web-main\server.js
var env = process.env.NODE_ENV = process.env.NODE_ENV || 'development';
var express = require('express'),
logger = require('morgan'),
profileManager = require('./lib/profile-manager');
var app = express();
app.use(logger('dev'));
app.listen(4000, function() {
console.log('web.main listening on port 4000...');
profileManager.connect();
profileManager.disconnect();
});
web-main\lib\profile-manager.js
var net = require('net');
var client = new net.Socket();
var _connect = function() {
client.connect(process.env.SRV_PROFILE_PORT, function() {
console.log('CONNECTED TO: ' + process.env.SRV_PROFILE);
// Write a message to the socket as soon as the client is connected, the server will receive it as message from the client
client.write({
action: 'doSomething',
data: '1234',
source: 'web-main'
});
});
// Add a 'data' event handler for the client socket
// data is what the server sent to this socket
client.on('data', function(data) {
console.log('DATA: ' + data);
});
// Add a 'close' event handler for the client socket
client.on('close', function() {
console.log('Connection closed');
});
};
var _disconnect = function() {
// Close the client socket completely
client.destroy();
};
module.exports = {
connect: _connect,
disconnect: _disconnect
};

Have you tried to run it locally, w/o docker? I made this an exercise for myself and found multiple issues:
As mentioned before, server should bind to 0.0.0.0 to listen on all interfaces
Docker file for server must EXPOSE 4000 (which it seemingly does in your example, since the link works)
Socket.connect() does not accept the docker-style url (i.e. 'tcp://172.17.0.12:4000'), it must be an options object like {host: 'bla', port: '1337'} - use Node's url module to parse the variable provided by docker.
As far as I can see, your client immediately disconnects right after it initiates the connection (the disconnect() call in profile-manager.js)
You can't just write an object to the socket. Sensible choice for encoding would be JSON.stringify()
Moreover, since data received via the socket is a Buffer, using data.toString('utf-8') should result in human-readable output.
Working example can be found on https://github.com/joerx/node-docker-tcp-sockets

If I'm reading your code correctly, you're profile service is only listening on 127.0.0.1. However, the web app is calling from another IP Address. Different containers have different IPs. So the profile service is denying the incoming connection because it is coming in to an invalid IP.

Related

Net Socket Connections in OpenShift

I am trying to create a net socket Nodejs server for my embedded device to send data to on OpenShift.
I am trying to create this simple echo service
var net = require('net');
var HOST = process.env.OPENSHIFT_NODEJS_IP;
var PORT = process.env.OPENSHIFT_NODEJS_PORT || 3000;
console.log('IP:' + HOST + ' Port:' + PORT);
var server = net.createServer(function(connection) {
console.log('client connected');
connection.on('end', function() {console.log('client disconnected');});
connection.write('Hello World!\r\n');
connection.pipe(connection);
});
server.listen(PORT, HOST , function() {
console.log('server is listening');
});
I according to OpenShift's Port Binding Guide I had my client application connect to Port 8000.
For Testing I am using the following code from my desktop machine.
var net = require('net');
var HOST = 'nodejs-myapplication.rhcloud.com';
var PORT = 8000;
var client = net.connect(PORT, HOST, function() {
console.log('connected to server!');
});
client.on('data', function(data) {
console.log(data.toString());
client.end();
});
client.on('end', function() {
console.log('disconnected from server');
});
The Client Scripts gets to Connected to server and gets stuck there itself. Nothing takes place after that.
Now if I open the address nodejs-myapplication.rhcloud.com:8000 in my browser, the NodeJS Server logs a client connected and disconnected, but when the NodeClient is connected the server doesn't show any update. The Node Client just says connected and stays there without doing anything.
If I run the same scripts locally it works fine, ( Locally i use HOst as 127.0.0.1 and port as 3000).
How can I get a TCP Application to connect to the Openshift NodeJS Server?
The Device will be sending ASCII output over the socket eg. $VAR,12,23,21\r\n
I need to be able to read that data, convert it to JSON and send it out to another server.
It has to be loaded on a platform like DigitalOcean with a firewall enabled.
OpenShift doesn't allow custom ports by default so need a workaround for that.

The nodejs net.createServer function results in a blank server.address value in Bluemix

The net module has a createServer function that allows you to create a network wrapper. This works fine on a local runtime of Nodejs, but when running in Bluemix it is unable to determine the host address. The server seems to get created, but upon further inspection I find the server.address to be blank.
var tls = require('tls');
var fs = require('fs');
var net = require('net');
var tunnelHost = (process.env.VCAP_APP_HOST || 'localhost');
var tunnelPort = 8888;
var server;
var gatewayOptions = {
host: 'http://cap-sg-prd-5.integration.ibmcloud.com/',
port: '15133',
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
ca: fs.readFileSync('ca.pem')
};
console.log("starting createSecureTunnel");
//create a server end point to use as a network wrapper for the secure gateway
server = net.createServer(function (connListener){
console.log('net server created');
connListener.on('end', function() {
console.log('client disconnected');
});
connListener.on('uncaughtException', function(err){
console.log('exception caught: ' + JSON.stringify(err));
});
//connect to farside, local/private server
connectFarside(connListener, function(err, remoteSocket){
if (err){
console.log(err);
}
console.log('connection made');
remoteSocket.pipe(connListener);
console.log('remote socket connecte to local connListener');
connListener.pipe(remoteSocket);
console.log('local connListener connected to remote socket');
});
});
//setup listener for network wrapper
server.listen(tunnelPort, tunnelHost, function(){
console.log('tunnel created at: ' + tunnelHost +":"+ tunnelPort); //.address +":"+ server.address().port);
});
//createa a TLS connection to the secure gateway
function connectFarside(conn, callback) {
console.log("starting connectFarside");
try {
console.log("initiating farside connection");
var socket = tls.connect(gatewayOptions, function(){
console.log("tunnel connected to " + gatewayOptions.host +":"+ gatewayOptions.port);
callback(null, socket);
});
socket.on("error", function(err){
console.log("Socket error: " + JSON.stringify(err));
});
} catch(err) {
console.log(err);
callback(err);
}
}
Bluemix gives your app a port to run on, this is the reason it is not working in Bluemix. You are starting to start your app on port 8888 with the following line of code.
var tunnelPort = 8888;
It should be changed to
var tunnelPort = process.env.VCAP_APP_PORT || 8888;
The above line will read an environment variable called VCAP_PORT where Bluemix assigns a port to your app, if it is not running Bluemix it will run on port 8888.
Your app will be accessible over the web on port 80 and 443. Bluemix will load balance to your app for you.
You can specify the server address when listening to the server
var net = require('net')
var server = net.createServer(handler)
server.listen(port, address)
Try with address = '0.0.0.0' and see if it works
Partially solved by using the cf-autoconfig module. It helps to reconfigure modules for use on Cloud Foundry platforms. By including this as the first line in my app, it mostly works. It doesn't use the port number. But at least I can access the wrapper.
So I added this as the first line
require("cf-autoconfig");
Then I changed the server.listen to this
//setup listener for network wrapper
server.listen(tunnelPort, function(){
console.log('tunnel created at: ' + tunnelHost +":"+ tunnelPort); //.address +":"+ server.address().port);
});
Now if I use my app name, I can connect to the server created by net.createServer().
I would still like to know how to get the port to work, so this can be used inside of a web application to provide the tunneling.

Socket.io creating multiple connections

I'm integrating socket.io into my project. I'm using the code below and it's creating 6 connections after the first request. Is this normal?
server.listen(
port,
function()
{
console.log('Node.js server listening on port ' + port);
}
);
server.on(
'connection',
function(socket)
{
console.log('socket.io connection');
}
);
And here is the console.log output:
Node.js server listening on port 3000
socket.io connection
socket.io connection
socket.io connection
socket.io connection
socket.io connection
socket.io connection
You get this result because (as far as I understand) your server object is an instance of node's http.Server class, and is not connected with Socket.IO at all. In your example, 'connection' event is being fired on any request the your node server. It looks like browser sends 6 requests to your node server: page, favicon.ico, and 4 other requests (it might be images, javascripts, css, etc.).
To integrate socket.io into your project you may use the following code:
var http = require('http');
var sio = require('socket.io');
var server = http.createServer(function(req, res) {
//you request handler here
});
var io = sio(server);
io.on('connection', function(socket) {
console.log('socket connected');
//now you can emit and listen messages
});
var port = 3000;
server.listen(port, function() {
console.log('Node.js server listening on port ' + port);
});
And, of course, the official documentation might be very helpful. Good luck :)

Node js net event : connect vs connection

When is the connection event of the net.Server fired vs the net.Socket connect event. Are they the same event? I have seen a code example where the handler for the net.createServer function (which handlers the connection event) also has the following code in it
var server = net.createServer(function (client) {
var id = client.remoteAddress + ':' + client.remotePort;
client.on('connect', function() {
channel.emit('join', id, client);
});
client.on('data', function(data) {
data = data.toString();
channel.emit('broadcast', id, data);
});
});
Is this incorrect? Is this listener not needed/never hit..i.e. the emit should be outside the listener.
Based on the code you post here, I'll assume you are reading Node.js in Action and it's the sample code that cause you the problem. If this is the case, you may reference to this similar question: NodeJS events on('connect') error.
Summary
In short, these 2 events are received by 2 different objects.
connect event is received by socket object and is emitted "when a socket connection is successfully established". On ther other hand, connection event is received by server object and is emitted "when a new connection is made".
Notice when a client hit node and create a socket, the callback for net.createServer(callback) is automatically called, thus you don't have to manually register another event handler on this client-server socket, which is client.on('connect', function() { }); in your code.
Concepts
As #3y3 mentioned, there are 2 ways to use net module in node.js, which are createConnection and createServer.
To better understand how it works, you may use this diagram as sample:
net.createServer
When you require net module and createServer, you basically create a server for clients (browser via http, terminal via telnet, etc.) to connect in.
Consider the following codes:
var net = require('net');
var events = require('events');
var channel = new events.EventEmitter();
var server = net.createServer(function (socket) {
var id = socket.remoteAddress + ': ' + client.remotePort;
console.log('Server connected', id);
channel.emit('join', id, socket);
socket.on('data', function(data) {
data = data.toString();
channel.emit('broadcast', id, data);
});
}).listen(8888);
In this case, the parameter in the callback of createServer is the "socket" between server and client. In the sample code, the author of Node.js in Action call it client, which might be a little confusing for you, but that's actually the same concept: it's a client related object which contains methods for your server to do something.
Notice that this callback is registered on the server and called at the same time the connection is built. This is a result of connection event which is emitted to the server when the client hit port 8888 on server in this case. Any functions you put inside this callback will be execute immediately. In our case, there are three things we did:
Console.log the client id on server
Emit a 'join' event to channel object
Register a event listener for 'data' event (i.e. called when there's data sending from client)
There's no need for client.on('connect', function() { // do something }), and since the callback for server connection event is the one we just mentioned. So what's the purpose of clinet.on('connect')?
net.createConnection
By utilizing this function, you create "a connection to the server" rather then create a server itself. In other words, it just like you open a terminal and use telnet to connect to the server, only that this happens in your server code base (i.e. in your node environment, as the diagram Client x shows)
Consider the following code: (based on #3y3's example again):
var Client = net.createConnection;
var client = Client({port: 8888, localAddress: '127.0.0.1', localPort: 51000});
client.on('connect', function() {
var id = this.localAddress + ': ' + this.localPort;
console.log('Client connected', id);
});
Here, we build a client in node and connect to the server through port 8888, which is what we defined previously. localPort and localAddress are defined arbitrary, just a mimic for a terminal connection in local environment.
We then register an event handler on this client, so when this client is connected to the server, it will receive a connect event and execute this event handler. If you run both snippets in the same file with node, you'll see both
Client connected 127.0.0.1: 51000
Server connected ::ffff:127.0.0.1: 51000
in the console.
Conclusion
If you want to do something when remote clients connect to the server, simply put lines you want to execute in the callback function of net.createServer(function(socket) { // your lines here }).
Use client.on('connect', function() {}); for other manually build clients in your node environment.
For further information about socket object and server object, you may refer to the official document here.
You can play with the following revised codes in your case:
var events = require('events');
var net = require('net');
var channel = new events.EventEmitter();
channel.clients = {};
channel.subscriptions = {};
channel.on('join', function (id, client) {
this.clients[id] = client;
this.subscriptions[id] = function (senderId, message) {
if (senderId !== id) {
this.clients[id].write(message);
}
};
this.on('broadcast', this.subscriptions[id]);
});
var server = net.createServer(function (client) {
var id = client.remoteAddress + ': ' + client.remotePort;
console.log('Server connected', id);
channel.emit('join', id, client);
client.on('data', function(data) {
data = data.toString();
channel.emit('broadcast', id, data);
});
});
server.listen(8888);
var Client = net.createConnection;
var client = Client({port: 8888, localAddress: '127.0.0.1', localPort: 51000});
client.on('connect', function() {
var id = this.localAddress + ': ' + this.localPort;
console.log('Client connected', id);
});
If your code is same as:
var Client = require('net').createConnection,
client = Client({port:4321});
client.on('connect', function(){
//channel.emit('join', id, client);
channel.emit('join', id, this); //avoid bad closure
});
It is valid code and when connect will emited, the server part emits connection, creates socket object and pass it to connection callback
UPDATE:
Your code is incorrect. You pass to createServer the callback for connection event it same as:
var server = net.createServer();
server.on('connection', callback);
In callback you have yet connected socket object. This is correct:
var server = net.createServer(function (client) {
var id = client.remoteAddress + ':' + client.remotePort;
channel.emit('join', id, client);
client.on('data', function(data) {
data = data.toString();
channel.emit('broadcast', id, data);
});
});
Please read the official Nodejs document for module 'net'.
The crux of your matter is, you have a server socket that you have named 'client' (a confusing name) and it is not a client socket. For a client socket you can subscribe to 'connect' event and not for a server socket.
net.createServer(callback) takes a callback which is called back when a new connection from a client occurs and the callback is passed the server socket. What you are referring to in function(client) is essentially a server socket and the term used 'client' is misleading. Please see the Node.js 'net' module example of createServer. For a server socket all the events listed (e.g data, timeout, end, close etc) are valid except the 'connect' event.
On the contrary to a server socket, if you are to create a client program where you called net.createConnection that will return you a client socket. Of that socket you subscribe to the 'connect' event.

Listening to port that Windows app uses results in EADDRINUSE

I have a Windows application that sends/receives TCP messages on a certain port. I hooked up the simple echo example on the Node.js website but I get the EADDRINUSE error.
I assume this is because my app is using that port.
What I was trying to do is to listen to that port and then hook a browser up to listen to what the server is sending out.
Ok, think I got it working:
var sys = require("sys"),
net = require("net");
var client = net.createConnection(10100);
client.setEncoding("UTF8");
client.addListener("connect", function() {
sys.puts("Client connected.");
// close connection after 2sec
setTimeout(function() {
sys.puts("Sent to server: close");
client.write("close", "UTF8");
}, 2000);
});
client.addListener("data", function(data) {
sys.puts("Response from server: " + data);
if (data == "close") client.end();
});
client.addListener("close", function(data) {
sys.puts("Disconnected from server");
});
If you want to intercept data sent to and from an applicaiton, I would recommend using Wireshark. If you want to capture this data from an applicaiton, I would recommend you use WinPCap.

Resources