NGROK + WEB-SERVER + NODE.JS + WEBSOCKET + NPM - node.js

I have a web server on localhost and I connect to this server with my phone via ngrock. Up here very well, since ngrock creates a url, for example http://94815c96.ngrok.io that redirects me to localhost:8888, where I can see my project and files.
Then I need to connect my web-server through websocket and make a chat application, for this I have installed npm and node.js and I run the command node filename.js, which this filename.js file creates a socket that listens on port 1337 and on the side of the web-server opens the websocket on port 1337. If i do it via web-browser on my local device it works, but when I try it via mobile phone with the ngrock url it return an error that cannot connect to WebSocket Server.
The code that I use is simple. I have one html called frontend.html to view and output the chat application, another file called chat-client.js that opens the connection to websocket on port 1337 and the last file is called chat-server.js that listens on port 1337. The code for each file is this one below here:
frontend.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebSockets - Simple chat</title>
<style>
* { font-family:tahoma; font-size:12px; padding:0px; margin:0px; }
p { line-height:18px; }
div { width:500px; margin-left:auto; margin-right:auto;}
#content { padding:5px; background:#ddd; border-radius:5px; overflow-y: scroll;
border:1px solid #CCC; margin-top:10px; height: 160px; }
#input { border-radius:2px; border:1px solid #ccc;
margin-top:10px; padding:5px; width:400px; }
#status { width:88px; display:block; float:left; margin-top:15px; }
</style>
</head>
<body>
<div id="content"></div>
<div>
<span id="status">Connecting...</span>
<input type="text" id="input" disabled="disabled" />
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="./chat-frontend.js"></script>
</body>
</html>
chat-client.js:
$(function () {
"use strict";
// for better performance - to avoid searching in DOM
var content = $('#content');
var input = $('#input');
var status = $('#status');
// my color assigned by the server
var myColor = false;
// my name sent to the server
var myName = false;
// if user is running mozilla then use it's built-in WebSocket
window.WebSocket = window.WebSocket || window.MozWebSocket;
// if browser doesn't support WebSocket, just show some notification and exit
if (!window.WebSocket) {
content.html($('<p>', { text: 'Sorry, but your browser doesn\'t '
+ 'support WebSockets.'} ));
input.hide();
$('span').hide();
return;
}
// open connection
var connection = new WebSocket('ws://127.0.0.1:1337');
connection.onopen = function () {
// first we want users to enter their names
input.removeAttr('disabled');
status.text('Choose name:');
};
connection.onerror = function (error) {
// just in there were some problems with conenction...
content.html($('<p>', { text: 'Sorry, but there\'s some problem with your '
+ 'connection or the server is down.' } ));
};
// most important part - incoming messages
connection.onmessage = function (message) {
// try to parse JSON message. Because we know that the server always returns
// JSON this should work without any problem but we should make sure that
// the massage is not chunked or otherwise damaged.
try {
var json = JSON.parse(message.data);
} catch (e) {
console.log('This doesn\'t look like a valid JSON: ', message.data);
return;
}
// NOTE: if you're not sure about the JSON structure
// check the server source code above
if (json.type === 'color') { // first response from the server with user's color
myColor = json.data;
status.text(myName + ': ').css('color', myColor);
input.removeAttr('disabled').focus();
// from now user can start sending messages
} else if (json.type === 'history') { // entire message history
// insert every single message to the chat window
for (var i=0; i < json.data.length; i++) {
addMessage(json.data[i].author, json.data[i].text,
json.data[i].color, new Date(json.data[i].time));
}
} else if (json.type === 'message') { // it's a single message
input.removeAttr('disabled'); // let the user write another message
addMessage(json.data.author, json.data.text,
json.data.color, new Date(json.data.time));
} else {
console.log('Hmm..., I\'ve never seen JSON like this: ', json);
}
};
/**
* Send mesage when user presses Enter key
*/
input.keydown(function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
if (!msg) {
return;
}
// send the message as an ordinary text
connection.send(msg);
$(this).val('');
// disable the input field to make the user wait until server
// sends back response
input.attr('disabled', 'disabled');
// we know that the first message sent from a user their name
if (myName === false) {
myName = msg;
}
}
});
/**
* This method is optional. If the server wasn't able to respond to the
* in 3 seconds then show some error message to notify the user that
* something is wrong.
*/
setInterval(function() {
if (connection.readyState !== 1) {
status.text('Error');
input.attr('disabled', 'disabled').val('Unable to comminucate '
+ 'with the WebSocket server.');
}
}, 3000);
/**
* Add message to the chat window
*/
function addMessage(author, message, color, dt) {
content.prepend('<p><span style="color:' + color + '">' + author + '</span> # ' +
+ (dt.getHours() < 10 ? '0' + dt.getHours() : dt.getHours()) + ':'
+ (dt.getMinutes() < 10 ? '0' + dt.getMinutes() : dt.getMinutes())
+ ': ' + message + '</p>');
}
});
chat-server.js
// http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
"use strict";
// Optional. You will see this name in eg. 'ps' or 'top' command
process.title = 'node-chat';
// Port where we'll run the websocket server
var webSocketsServerPort = 1337;
// websocket and http servers
var webSocketServer = require('websocket').server;
var http = require('http');
/**
* Global variables
*/
// latest 100 messages
var history = [ ];
// list of currently connected clients (users)
var clients = [ ];
/**
* Helper function for escaping input strings
*/
function htmlEntities(str) {
return String(str).replace(/&/g, '&').replace(/</g, '<')
.replace(/>/g, '>').replace(/"/g, '"');
}
// Array with some colors
var colors = [ 'red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange' ];
// ... in random order
colors.sort(function(a,b) { return Math.random() > 0.5; } );
/**
* HTTP server
*/
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
});
/**
* WebSocket server
*/
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server. WebSocket request is just
// an enhanced HTTP request. For more info http://tools.ietf.org/html/rfc6455#page-6
httpServer: server
});
// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
// accept connection - you should check 'request.origin' to make sure that
// client is connecting from your website
// (http://en.wikipedia.org/wiki/Same_origin_policy)
var connection = request.accept(null, request.origin);
// we need to know client index to remove them on 'close' event
var index = clients.push(connection) - 1;
var userName = false;
var userColor = false;
console.log((new Date()) + ' Connection accepted.');
// send back chat history
if (history.length > 0) {
connection.sendUTF(JSON.stringify( { type: 'history', data: history} ));
}
// user sent some message
connection.on('message', function(message) {
if (message.type === 'utf8') { // accept only text
if (userName === false) { // first message sent by user is their name
// remember user name
userName = htmlEntities(message.utf8Data);
// get random color and send it back to the user
userColor = colors.shift();
connection.sendUTF(JSON.stringify({ type:'color', data: userColor }));
console.log((new Date()) + ' User is known as: ' + userName
+ ' with ' + userColor + ' color.');
} else { // log and broadcast the message
console.log((new Date()) + ' Received Message from '
+ userName + ': ' + message.utf8Data);
// we want to keep history of all sent messages
var obj = {
time: (new Date()).getTime(),
text: htmlEntities(message.utf8Data),
author: userName,
color: userColor
};
history.push(obj);
history = history.slice(-100);
// broadcast message to all connected clients
var json = JSON.stringify({ type:'message', data: obj });
for (var i=0; i < clients.length; i++) {
clients[i].sendUTF(json);
}
}
}
});
// user disconnected
connection.on('close', function(connection) {
if (userName !== false && userColor !== false) {
console.log((new Date()) + " Peer "
+ connection.remoteAddress + " disconnected.");
// remove user from the list of connected clients
clients.splice(index, 1);
// push back user's color to be reused by another user
colors.push(userColor);
}
});
});
Command to execute ngrok
Execute node with chat-server.js
Error returned by ngrok

You need to expose to the world your second port 1337, which is used by WebSockets. You would need to have 2 separate ngrok instances running and forwarding 1337 and 8888 ports. Also in chat-client.js line "ws://127.0.0.1:1337" should be changed for the new ngrok forwarding url (for example ws://111111.ngrok.io)

Related

navigator.getUserMedia is not a function using WebRTC

I try to build a video group confference stream with some people and i use WebRTC (From here: https://github.com/anoek/webrtc-group-chat-example) and as server i use VPS Centos 7 with node js installed and when i try to start my webrtc server, in the browser i see this error on accessing the ip with port
This error:
(index):260 Uncaught TypeError: navigator.getUserMedia is not a function
at setup_local_media ((index):260)
at Socket.<anonymous> ((index):50)
at Socket.Emitter.emit (socket.io-1.4.5.js:3)
at Socket.emit (socket.io-1.4.5.js:3)
at Socket.onconnect (socket.io-1.4.5.js:3)
at Socket.onpacket (socket.io-1.4.5.js:3)
at Manager.<anonymous> (socket.io-1.4.5.js:3)
at Manager.Emitter.emit (socket.io-1.4.5.js:3)
at Manager.ondecoded (socket.io-1.4.5.js:2)
at Decoder.<anonymous> (socket.io-1.4.5.js:3)
JS code:
/*************/
/*** SETUP ***/
/*************/
var express = require('express');
var bodyParser = require('body-parser')
var cors = require('cors')
var main = express()
main.use(cors())
var server = main.listen(8000);
var io = require('socket.io').listen(server);
main.get('/', function(req, res){ res.sendFile(__dirname + '/client.html'); });
/*************************/
/*** INTERESTING STUFF ***/
/*************************/
var channels = {};
var sockets = {};
/**
* Users will connect to the signaling server, after which they'll issue a "join"
* to join a particular channel. The signaling server keeps track of all sockets
* who are in a channel, and on join will send out 'addPeer' events to each pair
* of users in a channel. When clients receive the 'addPeer' even they'll begin
* setting up an RTCPeerConnection with one another. During this process they'll
* need to relay ICECandidate information to one another, as well as SessionDescription
* information. After all of that happens, they'll finally be able to complete
* the peer connection and will be streaming audio/video between eachother.
*/
io.sockets.on('connection', function (socket) {
socket.channels = {};
sockets[socket.id] = socket;
console.log("["+ socket.id + "] connection accepted");
socket.on('disconnect', function () {
for (var channel in socket.channels) {
part(channel);
}
console.log("["+ socket.id + "] disconnected");
delete sockets[socket.id];
});
socket.on('join', function (config) {
console.log("["+ socket.id + "] join ", config);
var channel = config.channel;
var userdata = config.userdata;
if (channel in socket.channels) {
console.log("["+ socket.id + "] ERROR: already joined ", channel);
return;
}
if (!(channel in channels)) {
channels[channel] = {};
}
for (id in channels[channel]) {
channels[channel][id].emit('addPeer', {'peer_id': socket.id, 'should_create_offer': false});
socket.emit('addPeer', {'peer_id': id, 'should_create_offer': true});
}
channels[channel][socket.id] = socket;
socket.channels[channel] = channel;
});
function part(channel) {
console.log("["+ socket.id + "] part ");
if (!(channel in socket.channels)) {
console.log("["+ socket.id + "] ERROR: not in ", channel);
return;
}
delete socket.channels[channel];
delete channels[channel][socket.id];
for (id in channels[channel]) {
channels[channel][id].emit('removePeer', {'peer_id': socket.id});
socket.emit('removePeer', {'peer_id': id});
}
}
socket.on('part', part);
socket.on('relayICECandidate', function(config) {
var peer_id = config.peer_id;
var ice_candidate = config.ice_candidate;
console.log("["+ socket.id + "] relaying ICE candidate to [" + peer_id + "] ", ice_candidate);
if (peer_id in sockets) {
sockets[peer_id].emit('iceCandidate', {'peer_id': socket.id, 'ice_candidate': ice_candidate});
}
});
socket.on('relaySessionDescription', function(config) {
var peer_id = config.peer_id;
var session_description = config.session_description;
console.log("["+ socket.id + "] relaying session description to [" + peer_id + "] ", session_description);
if (peer_id in sockets) {
sockets[peer_id].emit('sessionDescription', {'peer_id': socket.id, 'session_description': session_description});
}
});
});
Html code:
<!doctype html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<!-- This adapter.js file de-prefixes the webkit* and moz* prefixed RTC
methods. When RTC becomes a more solid standard, this adapter should no
longer be necessary. -->
<!-- <script src="https://webrtc.googlecode.com/svn/trunk/samples/js/base/adapter.js"></script> -->
<style>
html, body {
background-color: #333;
}
video {
width: 320px;
height: 240px;
border: 1px solid black;
}
</style>
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script>
/** CONFIG **/
var SIGNALING_SERVER = "http://127.0.0.1:8000";
var USE_AUDIO = true;
var USE_VIDEO = true;
var DEFAULT_CHANNEL = 'some-global-channel-name';
var MUTE_AUDIO_BY_DEFAULT = false;
/** You should probably use a different stun server doing commercial stuff **/
/** Also see: https://gist.github.com/zziuni/3741933 **/
var ICE_SERVERS = [
{url:"stun:stun.l.google.com:19302"}
];
</script>
<script>
var signaling_socket = null; /* our socket.io connection to our webserver */
var local_media_stream = null; /* our own microphone / webcam */
var peers = {}; /* keep track of our peer connections, indexed by peer_id (aka socket.io id) */
var peer_media_elements = {}; /* keep track of our <video>/<audio> tags, indexed by peer_id */
function init() {
console.log("Connecting to signaling server");
signaling_socket = io(SIGNALING_SERVER);
signaling_socket = io();
signaling_socket.on('connect', function() {
console.log("Connected to signaling server");
setup_local_media(function() {
/* once the user has given us access to their
* microphone/camcorder, join the channel and start peering up */
join_chat_channel(DEFAULT_CHANNEL, {'whatever-you-want-here': 'stuff'});
});
});
signaling_socket.on('disconnect', function() {
console.log("Disconnected from signaling server");
/* Tear down all of our peer connections and remove all the
* media divs when we disconnect */
for (peer_id in peer_media_elements) {
peer_media_elements[peer_id].remove();
}
for (peer_id in peers) {
peers[peer_id].close();
}
peers = {};
peer_media_elements = {};
});
function join_chat_channel(channel, userdata) {
signaling_socket.emit('join', {"channel": channel, "userdata": userdata});
}
function part_chat_channel(channel) {
signaling_socket.emit('part', channel);
}
/**
* When we join a group, our signaling server will send out 'addPeer' events to each pair
* of users in the group (creating a fully-connected graph of users, ie if there are 6 people
* in the channel you will connect directly to the other 5, so there will be a total of 15
* connections in the network).
*/
signaling_socket.on('addPeer', function(config) {
console.log('Signaling server said to add peer:', config);
var peer_id = config.peer_id;
if (peer_id in peers) {
/* This could happen if the user joins multiple channels where the other peer is also in. */
console.log("Already connected to peer ", peer_id);
return;
}
var peer_connection = new RTCPeerConnection(
{"iceServers": ICE_SERVERS},
{"optional": [{"DtlsSrtpKeyAgreement": true}]} /* this will no longer be needed by chrome
* eventually (supposedly), but is necessary
* for now to get firefox to talk to chrome */
);
peers[peer_id] = peer_connection;
peer_connection.onicecandidate = function(event) {
if (event.candidate) {
signaling_socket.emit('relayICECandidate', {
'peer_id': peer_id,
'ice_candidate': {
'sdpMLineIndex': event.candidate.sdpMLineIndex,
'candidate': event.candidate.candidate
}
});
}
}
peer_connection.onaddstream = function(event) {
console.log("onAddStream", event);
var remote_media = USE_VIDEO ? $("<video>") : $("<audio>");
remote_media.attr("autoplay", "autoplay");
if (MUTE_AUDIO_BY_DEFAULT) {
remote_media.attr("muted", "true");
}
remote_media.attr("controls", "");
peer_media_elements[peer_id] = remote_media;
$('body').append(remote_media);
attachMediaStream(remote_media[0], event.stream);
}
/* Add our local stream */
peer_connection.addStream(local_media_stream);
/* Only one side of the peer connection should create the
* offer, the signaling server picks one to be the offerer.
* The other user will get a 'sessionDescription' event and will
* create an offer, then send back an answer 'sessionDescription' to us
*/
if (config.should_create_offer) {
console.log("Creating RTC offer to ", peer_id);
peer_connection.createOffer(
function (local_description) {
console.log("Local offer description is: ", local_description);
peer_connection.setLocalDescription(local_description,
function() {
signaling_socket.emit('relaySessionDescription',
{'peer_id': peer_id, 'session_description': local_description});
console.log("Offer setLocalDescription succeeded");
},
function() { Alert("Offer setLocalDescription failed!"); }
);
},
function (error) {
console.log("Error sending offer: ", error);
});
}
});
/**
* Peers exchange session descriptions which contains information
* about their audio / video settings and that sort of stuff. First
* the 'offerer' sends a description to the 'answerer' (with type
* "offer"), then the answerer sends one back (with type "answer").
*/
signaling_socket.on('sessionDescription', function(config) {
console.log('Remote description received: ', config);
var peer_id = config.peer_id;
var peer = peers[peer_id];
var remote_description = config.session_description;
console.log(config.session_description);
var desc = new RTCSessionDescription(remote_description);
var stuff = peer.setRemoteDescription(desc,
function() {
console.log("setRemoteDescription succeeded");
if (remote_description.type == "offer") {
console.log("Creating answer");
peer.createAnswer(
function(local_description) {
console.log("Answer description is: ", local_description);
peer.setLocalDescription(local_description,
function() {
signaling_socket.emit('relaySessionDescription',
{'peer_id': peer_id, 'session_description': local_description});
console.log("Answer setLocalDescription succeeded");
},
function() { Alert("Answer setLocalDescription failed!"); }
);
},
function(error) {
console.log("Error creating answer: ", error);
console.log(peer);
});
}
},
function(error) {
console.log("setRemoteDescription error: ", error);
}
);
console.log("Description Object: ", desc);
});
/**
* The offerer will send a number of ICE Candidate blobs to the answerer so they
* can begin trying to find the best path to one another on the net.
*/
signaling_socket.on('iceCandidate', function(config) {
var peer = peers[config.peer_id];
var ice_candidate = config.ice_candidate;
peer.addIceCandidate(new RTCIceCandidate(ice_candidate));
});
/**
* When a user leaves a channel (or is disconnected from the
* signaling server) everyone will recieve a 'removePeer' message
* telling them to trash the media channels they have open for those
* that peer. If it was this client that left a channel, they'll also
* receive the removePeers. If this client was disconnected, they
* wont receive removePeers, but rather the
* signaling_socket.on('disconnect') code will kick in and tear down
* all the peer sessions.
*/
signaling_socket.on('removePeer', function(config) {
console.log('Signaling server said to remove peer:', config);
var peer_id = config.peer_id;
if (peer_id in peer_media_elements) {
peer_media_elements[peer_id].remove();
}
if (peer_id in peers) {
peers[peer_id].close();
}
delete peers[peer_id];
delete peer_media_elements[config.peer_id];
});
}
/***********************/
/** Local media stuff **/
/***********************/
function setup_local_media(callback, errorback) {
if (local_media_stream != null) { /* ie, if we've already been initialized */
if (callback) callback();
return;
}
/* Ask user for permission to use the computers microphone and/or camera,
* attach it to an <audio> or <video> tag if they give us access. */
console.log("Requesting access to local audio / video inputs");
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
attachMediaStream = function(element, stream) {
console.log('DEPRECATED, attachMediaStream will soon be removed.');
element.srcObject = stream;
};
navigator.getUserMedia({"audio":USE_AUDIO, "video":USE_VIDEO},
function(stream) { /* user accepted access to a/v */
console.log("Access granted to audio/video");
local_media_stream = stream;
var local_media = USE_VIDEO ? $("<video>") : $("<audio>");
local_media.attr("autoplay", "autoplay");
local_media.attr("muted", "true"); /* always mute ourselves by default */
local_media.attr("controls", "");
$('body').append(local_media);
attachMediaStream(local_media[0], stream);
if (callback) callback();
},
function() { /* user denied access to a/v */
console.log("Access denied for audio/video");
alert("You chose not to provide access to the camera/microphone, demo will not work.");
if (errorback) errorback();
});
}
</script>
</head>
<body onload='init()'>
<!--
the <video> and <audio> tags are all added and removed dynamically
in 'onAddStream', 'setup_local_media', and 'removePeer'/'disconnect'
-->
</body>
</html>
navigator.getUserMedia is only available in secure contexts (i.e. https or localhost) - is your server running on https?
Note that navigator.getUserMedia is deprecated, you should be using the promise-based navigator.mediaDevices.getUserMedia - Safari only supports the latter.

Create webrtc video, voice call and file transfer using node js step by step Q-A

If any one need to add information or edit some information they are welcome.
Hi,
First of all this questions main target is shear my experience to other developers who finding good tutorials about webrtc.i'm not going to explain about webrtc. In this i add scourse code that working webrtc Video, voice call and file transfer example tested by me.
I get webrtc information from https://webrtc.org/ and get nodejs
from https://nodejs.org/en/
Ok let get start
Is web rtc need ssl certificated?
If you doing experiment in you local PC server no need. but when you add the to live server Yes you need it.
How i get ssl certificate?
one of my friend help me to get that SSl. there plenty of tutorials for you read and watch
How i get turn and stun server?
if you go production level you need to setup those server , but for test your project you can get stun server and turn servers for free.
For Stun server - https://gist.github.com/zziuni/3741933
For Turn server - Use this link and create free one ( http://numb.viagenie.ca/ ).
I add my working code as a an answer below
This is Working code for webrtc with node js
This code and comment that in code not by me. They already there when i got the code. I cant find code original owner. but i thanks that developer.If some found that developer please edit this and add that developer link :)
var express = require('express');
var socket = require('socket.io');
var app = express();
var fs = require('fs');
var https = require('https');
// link your https certicate path
var options = {
key: fs.readFileSync('/../../etc/ssl/private/apache-selfsigned.key'),
cert: fs.readFileSync('/../../etc/ssl/certs/apache-selfsigned.crt')
};
var main = https.createServer(options, app);
var server = main.listen(8443, function() {
console.log('server up and running at %s port', 8443);
});
/*var server = app.listen(443, function () {
});*/
app.use(express.static('public'));
var io = socket(server);
/*************************/
/*** INTERESTING STUFF ***/
/*************************/
var channels = {};
var sockets = {};
/**
* Users will connect to the signaling server, after which they'll issue a "join"
* to join a particular channel. The signaling server keeps track of all sockets
* who are in a channel, and on join will send out 'addPeer' events to each pair
* of users in a channel. When clients receive the 'addPeer' even they'll begin
* setting up an RTCPeerConnection with one another. During this process they'll
* need to relay ICECandidate information to one another, as well as SessionDescription
* information. After all of that happens, they'll finally be able to complete
* the peer connection and will be streaming audio/video between eachother.
*/
io.on('connection', function (socket) {
var channel;
socket.channels = {};
sockets[socket.id] = socket;
console.log("[" + socket.id + "] connection accepted");
socket.on('disconnect', function () {
for (var channel in socket.channels) {
part(channel);
}
console.log("[" + socket.id + "] disconnected");
delete sockets[socket.id];
});
socket.on('join-room', function (config) {
if (config) {
channel = config.channel;
var userdata = config.userdata;
var userID = config.userdata.userID;
if (channel in socket.channels) {
console.log("[" + socket.id + "] ERROR: already joined ", channel);
return;
}
if (!(channel in channels)) {
channels[channel] = {};
}
for (id in channels[channel]) {
channels[channel][id].emit('addPeer-room', {'peer_id': socket.id, 'should_create_offer': false});
socket.emit('addPeer-room', {'peer_id': id, 'should_create_offer': true});
console.log("what is this id -> ", id);
}
console.log(config.userdata.name, ' joining room', config.channel);
socket.join(config.channel);
socket.broadcast.in(config.channel).emit('room-users', config);
channels[channel][socket.id] = socket;
socket.channels[channel] = channel;
}
});
function part(channel) {
console.log("[" + socket.id + "] part ");
if (!(channel in socket.channels)) {
console.log("[" + socket.id + "] ERROR: not in ", channel);
return;
}
delete socket.channels[channel];
delete channels[channel][socket.id];
for (id in channels[channel]) {
channels[channel][id].emit('removePeer', {'peer_id': socket.id});
socket.emit('removePeer', {'peer_id': id});
}
}
socket.on('part', part);
socket.on('relayICECandidate-room', function (config) {
var peer_id = config.peer_id;
var ice_candidate = config.ice_candidate;
console.log("[" + socket.id + "] relaying ICE candidate to [" + peer_id + "] ", ice_candidate);
if (peer_id in sockets) {
sockets[peer_id].emit('iceCandidate-room', {'peer_id': socket.id, 'ice_candidate': ice_candidate});
}
});
socket.on('relaySessionDescription-room', function (config) {
var peer_id = config.peer_id;
var session_description = config.session_description;
console.log("[" + socket.id + "] relaying session description to [" + peer_id + "] ", session_description);
if (peer_id in sockets) {
sockets[peer_id].emit('sessionDescription-room', {
'peer_id': socket.id,
'session_description': session_description
});
}
});
// this for file transfer
socket.on('file-send-room', function (file) {
console.log(file);
socket.to(channel).emit('file-out-room', file);
});
socket.on('file-send-room-result', function (file) {
console.log(file);
socket.to(channel).emit('file-out-room-result', file);
});
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://cdn.socket.io/socket.io-1.4.5.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 fileInput = document.querySelector('input#fileInput');
var downloadAnchor = document.querySelector('a#download');
// this function use to get url parameters
var room = getParameterByName('room');
var userID = getParameterByName('userid');
var name = getParameterByName('name');
/** CONFIG **/
var SIGNALING_SERVER = "https://xxx.xx.xx.xxx:8443"; //your node server addres or IP adress
var USE_AUDIO = true;
var USE_VIDEO = true;
var MUTE_AUDIO_BY_DEFAULT = false;
/** You should probably use a different stun server doing commercial stuff **/
/** Also see: https://gist.github.com/zziuni/3741933 **/
var ICE_SERVERS = [
{urls: "stun:stun.l.google.com:19302"},{
urls: 'turn:numb.viagenie.ca:3478',
credential: '12344', //your password
username: 'your#email.com'
}
];
var socket = null;
/* our socket.io connection to our webserver */
var local_media_stream = null;
/* our own microphone / webcam */
var peers = {};
/* keep track of our peer connections, indexed by peer_id (aka socket.io id) */
var peer_media_elements = {};
/* keep track of our <video>/<audio> tags, indexed by peer_id */
$(document).ready(function (a) {
socket = io(SIGNALING_SERVER);
socket = io();
//----------------------------------------------------------------------->>>>> Files Send Start
const BYTES_PER_CHUNK = 1200;
var file;
var currentChunk;
var fileInput = $('input[type=file]');
var fileReader = new FileReader();
function readNextChunk() {
var start = BYTES_PER_CHUNK * currentChunk;
var end = Math.min(file.size, start + BYTES_PER_CHUNK);
fileReader.readAsArrayBuffer(file.slice(start, end));
}
fileReader.onload = function () {
socket.emit('file-send-room-result', fileReader.result);
//p2pConnection.send( fileReader.result );
currentChunk++;
if (BYTES_PER_CHUNK * currentChunk < file.size) {
readNextChunk();
}
};
fileInput.on('change', function () {
file = fileInput[0].files[0];
currentChunk = 0;
// send some metadata about our file
// to the receiver
socket.emit('file-send-room', JSON.stringify({
fileName: file.name,
fileSize: file.size
}));
readNextChunk();
});
var incomingFileInfo;
var incomingFileData;
var bytesReceived;
var downloadInProgress = false;
socket.on('file-out-room', function (data) {
startDownload(data);
console.log(data);
});
socket.on('file-out-room-result', function (data) {
progressDownload(data);
console.log(data); });
function startDownload(data) {
incomingFileInfo = JSON.parse(data.toString());
incomingFileData = [];
bytesReceived = 0;
downloadInProgress = true;
console.log('incoming file <b>' + incomingFileInfo.fileName + '</b> of ' + incomingFileInfo.fileSize + ' bytes');
}
function progressDownload(data) {
bytesReceived += data.byteLength;
incomingFileData.push(data);
console.log('progress: ' + ((bytesReceived / incomingFileInfo.fileSize ) * 100).toFixed(2) + '%');
if (bytesReceived === incomingFileInfo.fileSize) {
endDownload();
}
}
function endDownload() {
downloadInProgress = false;
var blob = new Blob(incomingFileData);
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
var blob = new Blob(incomingFileData);
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = incomingFileInfo.fileName;
a.click();
window.URL.revokeObjectURL(url);
}
//==================================================================<<< Filse Send End
//------------------------ Funtion
function join_chat_channel(channel, userdata) {
socket.emit('join-room', {"channel": channel, "userdata": userdata});
}
socket.on('connect', function (userID) {
console.log("Connected to signaling server");
setup_local_media(function () {
/* once the user has given us access to their
* microphone/camcorder, join the channel and start peering up */
join_chat_channel(room, {'name': name, 'userID': userID});
});
});
socket.on('room-user', function (data) {
console.log(data);
$("#online-user").append('<tr><td>Name = ' + data.userdata.name + ' <br> User ID= ' + data.userdata.userID + '</td><td><button class="call" id="' + data.userdata.userID + '">Call</button></td></tr>');
});
$('body').on('click', '.call', function () {
var callerID = $(this).attr('id');
socket.emit('call', {"callToId": callerID, "callFromId": userID});
});
/**
* When we join a group, our signaling server will send out 'addPeer' events to each pair
* of users in the group (creating a fully-connected graph of users, ie if there are 6 people
* in the channel you will connect directly to the other 5, so there will be a total of 15
* connections in the network).
*/
socket.on('addPeer-room', function (config) {
console.log('Signaling server said to add peer:', config);
var peer_id = config.peer_id;
if (peer_id in peers) {
/* This could happen if the user joins multiple channels where the other peer is also in. */
console.log("Already connected to peer ", peer_id);
return;
}
var peer_connection = new RTCPeerConnection(
{"iceServers": ICE_SERVERS},
{"optional": [{"DtlsSrtpKeyAgreement": true}]} /* this will no longer be needed by chrome
* eventually (supposedly), but is necessary
* for now to get firefox to talk to chrome */
);
peers[peer_id] = peer_connection;
peer_connection.onicecandidate = function (event) {
if (event.candidate) {
socket.emit('relayICECandidate-room', {
'peer_id': peer_id,
'ice_candidate': {
'sdpMLineIndex': event.candidate.sdpMLineIndex,
'candidate': event.candidate.candidate
}
});
}
}
peer_connection.onaddstream = function (event) {
console.log("onAddStream", event);
var remote_media = USE_VIDEO ? $("<video>") : $("<audio>");
remote_media.attr("autoplay", "autoplay");
if (MUTE_AUDIO_BY_DEFAULT) {
remote_media.attr("muted", "true");
}
remote_media.attr("controls", "");
peer_media_elements[peer_id] = remote_media;
$('body').append(remote_media);
attachMediaStream(remote_media[0], event.stream);
}
/* Add our local stream */
peer_connection.addStream(local_media_stream);
/* Only one side of the peer connection should create the
* offer, the signaling server picks one to be the offerer.
* The other user will get a 'sessionDescription' event and will
* create an offer, then send back an answer 'sessionDescription' to us
*/
if (config.should_create_offer) {
console.log("Creating RTC offer to ", peer_id);
peer_connection.createOffer(
function (local_description) {
console.log("Local offer description is: ", local_description);
peer_connection.setLocalDescription(local_description,
function () {
socket.emit('relaySessionDescription-room',
{'peer_id': peer_id, 'session_description': local_description});
console.log("Offer setLocalDescription succeeded");
},
function () {
Alert("Offer setLocalDescription failed!");
}
);
},
function (error) {
console.log("Error sending offer: ", error);
});
}
});
/**
* Peers exchange session descriptions which contains information
* about their audio / video settings and that sort of stuff. First
* the 'offerer' sends a description to the 'answerer' (with type
* "offer"), then the answerer sends one back (with type "answer").
*/
socket.on('sessionDescription-room', function (config) {
console.log('Remote description received: ', config);
var peer_id = config.peer_id;
var peer = peers[peer_id];
var remote_description = config.session_description;
console.log(config.session_description);
var desc = new RTCSessionDescription(remote_description);
var stuff = peer.setRemoteDescription(desc,
function () {
console.log("setRemoteDescription succeeded");
if (remote_description.type == "offer") {
console.log("Creating answer");
peer.createAnswer(
function (local_description) {
console.log("Answer description is: ", local_description);
peer.setLocalDescription(local_description,
function () {
socket.emit('relaySessionDescription-room',
{'peer_id': peer_id, 'session_description': local_description});
console.log("Answer setLocalDescription succeeded");
},
function () {
Alert("Answer setLocalDescription failed!");
}
);
},
function (error) {
console.log("Error creating answer: ", error);
console.log(peer);
});
}
},
function (error) {
console.log("setRemoteDescription error: ", error);
}
);
console.log("Description Object: ", desc);
});
/**
* The offerer will send a number of ICE Candidate blobs to the answerer so they
* can begin trying to find the best path to one another on the net.
*/
socket.on('iceCandidate-room', function (config) {
var peer = peers[config.peer_id];
var ice_candidate = config.ice_candidate;
peer.addIceCandidate(new RTCIceCandidate(ice_candidate));
});
/**
* When a user leaves a channel (or is disconnected from the
* signaling server) everyone will recieve a 'removePeer' message
* telling them to trash the media channels they have open for those
* that peer. If it was this client that left a channel, they'll also
* receive the removePeers. If this client was disconnected, they
* wont receive removePeers, but rather the
* signaling_socket.on('disconnect') code will kick in and tear down
* all the peer sessions.
*/
socket.on('removePeer-room', function (config) {
console.log('Signaling server said to remove peer:', config);
var peer_id = config.peer_id;
if (peer_id in peer_media_elements) {
peer_media_elements[peer_id].remove();
}
if (peer_id in peers) {
peers[peer_id].close();
}
delete peers[peer_id];
delete peer_media_elements[config.peer_id];
});
});
function setup_local_media(callback, errorback) {
if (local_media_stream != null) { /* ie, if we've already been initialized */
if (callback) callback();
return;
}
/* Ask user for permission to use the computers microphone and/or camera,
* attach it to an <audio> or <video> tag if they give us access. */
console.log("Requesting access to local audio / video inputs");
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
attachMediaStream = function (element, stream) {
console.log('DEPRECATED, attachMediaStream will soon be removed.');
element.srcObject = stream;
};
navigator.getUserMedia({"audio": USE_AUDIO, "video": USE_VIDEO},
function (stream) { /* user accepted access to a/v */
console.log("Access granted to audio/video");
local_media_stream = stream;
var local_media = USE_VIDEO ? $("<video>") : $("<audio>");
local_media.attr("autoplay", "autoplay");
local_media.attr("muted", "true");
/* always mute ourselves by default */
local_media.attr("controls", "");
$('body').append(local_media);
attachMediaStream(local_media[0], stream);
if (callback) callback();
},
function () { /* user denied access to a/v */
console.log("Access denied for audio/video");
alert("You chose not to provide access to the camera/microphone, demo will not work.");
if (errorback) errorback();
});
}
</script>
</head>
<body>
<form id="fileInfo">
<input type="file" id="fileInput" name="files"/>
</form>
<a id="download"></a>
</body>
</html>

Simple Access-Control-Allow-Origin with net module

I have two servers one on port 80 which is a normal web server and than one on port 8080 that is a broadcast server that sends a text string with all the updates to show. However the browser gives me a CORS header 'Access-Control-Allow-Origin' missing. And is it any simple way for me to add the header with the net package? The server code looks as following:
Code to create the server:
var net = require('net');
var fs = require('fs');
var buffer = require('buffer');
var serverHost = "0.0.0.0";
var serverPort = 8080;
var splitter = require('./splitter.js');
var server = net.createServer(function(target) {
// Socket errors
target.on('error', function(error) {
console.log('Socket error: ', error.message);
});
target.on('data', function(data){
// TODO: Verify the data
var number = parseInt(data);
if(!isNaN(number)){
// Adds the client to the list
splitter.addClient(target, number);
}
else{
target.write("Match ID is not correctly formatted");
}
});
});
// Listening for any problems with the server
server.on('error', function(error) {
console.log("Error listening to the server!", error.message);
});
server.listen(serverPort, serverHost);
splitter code (Add client and send data):
var gameids = {1337:[]}
function addClient(client, gameID){
if(matchExists(gameID)){
console.log("Client", client.remoteAddress, "begun watching gameID", gameID,"\n");
var id = gameids[gameID].push(client);
}
else{
client.write("A match with the given ID does not exists");
}
}
function sendData(data, gameID){
if(gameids[gameID].length > 0){
for(i = 0; i < gameids[gameID].length;i++){
console.log(gameids[gameID][i]);
if(gameids[gameID][i].writable){
console.log("Sent data");
gameids[gameID][i].write(data);
}
}
}
}
The function send data is called from my main script which just takes the input from another server and then sends that out to all clients watching.

node.js server not always clearing data from previous run

I'm brand new to node.js (or javascript in general) and not sure what's going on here. Sometimes when I run my node.js server, right when it starts up it begins printing data from the last time it ran. Is there something that could be keeping everything from resetting on a new run? I have arduino XBee nodes connecting to this server. Here's my node.js code:
var SerialPort = require("serialport");
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var ON_DEATH = require('death');
// Set up serial port
var portName = process.argv[2],
portConfig = {
baudRate: 9600,
parser: SerialPort.parsers.readline("\n")
};
var sp = new SerialPort(portName, portConfig); // got rid of deprecation issue with sp4
// Respond with file when a GET request is made to the homepage: app.METHOD(PATH, HANDLER) '/'=homepage
app.get('/', function(req, res){
res.sendfile('index.html');
});
// Listen on the connection event for incoming sockets, and log it to the console.
io.on('connection', function(socket){
console.log('a user connected');
socket.on('disconnect', function(){
});
socket.on('chat message', function(msg){
io.emit('chat message', msg);
sp.write(msg + "\n");
});
});
// Make the http server listen on port 3000
http.listen(3000, function(){
console.log('listening on *:3000');
});
/////////////////////////////////////////////////////////////////////////////////////////////////
sp.on("open", function (err) {
if (err)
return console.log('Error opening port: ', err.message);
console.log('open');
// INFO VARIABLES
//var nodeCount = 0;
var pendingNodes = [];
var connectedNodes = [];
var buffer0;
var nodeInfo;
// Cleanup when termination signals are sent to process
ON_DEATH(function(signal, err) {
var death_msg = "Q";
sp.write(death_msg);
sp.close();
// zero out all variables
pendingNodes = [];
connectedNodes = [];
buffer0 = [];
nodeInfo = [];
console.log("\n\nSending reset signal to nodes.\n\n")
sp.flush(function(err,results){});
process.exit();
})
// listen for data, grab time, delimit
sp.on('data', function(data) {
var time = new Date();
buffer0 = data.split('\n'); // array of data till newline
// Split again into either "#ID" into [#ID] (if new broadcast), or "#ID Data" into [#ID, Data] (preconnected node)
nodeInfo = buffer0[0].split(' ');
//console.log(" nodeInfo: " + nodeInfo);
// DEBUGGING PRINT -- DELETE LATER
//console.log(" pendingNodes: " + pendingNodes + " connectedNodes: " + connectedNodes);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Receiving ID or [ID, Data] -- Testing all possible cases
// if first char
if (nodeInfo[0][0] == "#") { // could check up here for && no temp data too
// Brand new node (ID not in pendingNodes or connectedNodes)
if ((pendingNodes.indexOf(nodeInfo[0]) == -1) && (connectedNodes.indexOf(nodeInfo[0]) == -1)) {
console.log("Brand new node: " + nodeInfo[0])
pendingNodes.push(nodeInfo[0]);
}
// Pending node (ID in pendingNodes, but not in connectedNodes)
else if ((pendingNodes.indexOf(nodeInfo[0]) != -1) && ((connectedNodes.indexOf(nodeInfo[0]) == -1))) {
console.log(" Pending node: " + nodeInfo[0])
// send back ID to confirm handshake
sp.write(nodeInfo[0]);
// Remove from pendingNodes
var index = pendingNodes.indexOf(nodeInfo[0]);
if (index > -1) {
pendingNodes.splice(index, 1);
}
// Add to connectedNodes
//var
connectedNodes.push(nodeInfo[0]);
}
// Connected node (ID in connectedNodes, but not in pendingNodes)
else if ((pendingNodes.indexOf(nodeInfo[0]) == -1) && (connectedNodes.indexOf(nodeInfo[0]) != -1)) {
console.log(" Connected node: " + nodeInfo[0] + " " + nodeInfo[1]);
// Disconnect nodes with undefined temperatures to force restart
if (typeof nodeInfo[1] == 'undefined') {
console.log("Undefined data for node: " + nodeInfo[0] + ". Dropping connection to this node.");
var index = connectedNodes.indexOf(nodeInfo[0]);
if (index > -1) {
connectedNodes.splice(index, 1);
}
}
//var t = time.getTime();
// FIRST: fix bug -- sometimes temp showing as undefined
// Then:
// Update data properly
// Average data
// add 3rd element to nodes for time stamp, check that all timestamps are recent periodically
// Detect drop-offs and remove nodes
}
// Error (ID in both pendingNodes and connectedNodes [should not happen])
else {
console.log("Error: node exists in both pending and connected.")
}
}
else if (nodeInfo[0][0] != "#") {
// FOR DEBUGGING PURPOSES, DELETE LATER
console.log("Error 2: incoming data does not start with '#'.")
}
/* // placeholders for functionality to add
var CalcAverage = function() {
console.log("CalcAverage: * every 1 second(s) *");
}
setInterval(function(){ CalcAverage() },2000);
var CheckNodes = function() {
console.log("CheckNodes: * every 6 second(s) *");
}
setInterval(function(){ CheckNodes() },6000);
*/
}); // end data
}); // end open
And here's an example of old node data showing up somehow:
Some possibilities:
Old (or new data that you didn't know it sent) data from clients has been buffered somehow on the serial port, or socket.io auto-reconnects on the client browsers and starts sending data even though you didn't interact with the client browser web page.

node.js REPL over socket hangs up when CTRL + C or CTRL + D is pressed

I'm trying to use REPL over socket like a telnet connection, but when I press CTRL + C or CTRL + D, or an error is produced, the socket connection hangs and doesn't respond to anything.
The node.js code is:
var net = require("net");
var connections = 0;
// Socket port 5001
net.createServer(function (socket) {
connections += 1;
var server = require("repl").start(
'session:' + connections + ' > ',
socket
);
server.rli.on('exit', function() {
socket.end();
});
}).listen(5001);
// Console
var server = require("repl").start('session:' + (++connections) + ' > ', null);
and from other terminal I run:
telnet localhost 5001
What is the problem?
Perhaps is a linux problem.
I've tried a Telnet server example with node.
When I run on window, every key I press is processed by receiveData function, but in linux no.
In linux is processed line per line and not character per character.
Here is the code I've probed:
var net = require('net');
var sockets = [];
/*
* Cleans the input of carriage return, newline
*/
function cleanInput(data) {
return data.toString().replace(/(\r\n|\n|\r)/gm,"");
}
/*
* Method executed when data is received from a socket
*/
function receiveData(socket, data) {
var cleanData = cleanInput(data);
if(cleanData === "#quit") {
socket.end('Goodbye!\n');
}
else {
for(var i = 0; i<sockets.length; i++) {
if (sockets[i] !== socket) {
sockets[i].write(data);
}
}
}
socket.write('Data: ' + data);
}
/*
* Method executed when a socket ends
*/
function closeSocket(socket) {
var i = sockets.indexOf(socket);
if (i != -1) {
sockets.splice(i, 1);
}
}
/*
* Callback method executed when a new TCP socket is opened.
*/
function newSocket(socket) {
sockets.push(socket);
socket.write('Welcome to the Telnet server!\n');
socket.on('data', function(data) {
receiveData(socket, data);
})
socket.on('end', function() {
closeSocket(socket);
})
}
// Create a new server and provide a callback for when a connection occurs
var server = net.createServer(newSocket);
// Listen on port 8888
server.listen(8888);

Resources