I am new to the NodeJs. I have created a simple application using socket.io. I know that I can use ExpressJs framework but I am not getting overall idea as to how to separate the code and divide them into multiple pages so that debugging and maintenance is easily.
I am not understanding how to structure the code and where to write the socket.io code or any other NodeJs code in general.
Should I create new folder and files? If yes then how?
My code: app.js
var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);
var ent = require('ent');
var fs = require('fs');
var cntActiveProductSupport = 0;
var cntActiveCustomer = 0;
var support_socket_id = '';
app.get('/',function (req, res) {
res.sendFile(__dirname+'/index.html');
});
app.get('/product_support',function (req, res) {
res.sendFile(__dirname+'/admin.html');
});
io.sockets.on('connection',function(socket,username){
//Each Socket in Socket.IO is identified by a random, unguessable, unique identifier Socket#id
console.log(socket.id);
//total number of connections
console.log(io.engine.clientsCount);
socket.on('support_connected',function(username){
username = ent.encode(username);
socket.username = username;
socket.broadcast.emit('support_connected',username);
//get socket id of all connected sockets
Object.keys(io.sockets.sockets).forEach(function(id) {
console.log("ID:",id) // socketId
});
cntActiveProductSupport++;
support_socket_id = socket.id;
});
socket.on('new_client',function(username){
var client_socket_id = socket.id;
username = ent.encode(username);
socket.username = username;
socket.broadcast.emit('new_client',{ username: username, socket_id: socket.id, support_socket_id: support_socket_id });
socket.to(client_socket_id).emit('client_connected',{ username: username, support_socket_id: support_socket_id });
cntActiveCustomer++;
});
socket.on('message',function(data){
//socket.disconnect(true); use this if you want to disconnect client from server
//client which sent the message
var client_socket_id = socket.id;
var message = ent.encode(data.message);
// sending to individual socketid (private message)
//socket.to(socketId).emit('hey', 'I just met you');
socket.to(support_socket_id).emit('message', {username: socket.username,message:message, client_socket_id: client_socket_id});
});
socket.on('support_messaging',function(data){
message = ent.encode(data.message);
var customer_socket_id = data.customer_socket_id;
//socket.broadcast.emit('message', {username: socket.username,message:message});
// sending to individual socketid (private message)
socket.broadcast.to(customer_socket_id).emit('message', {username: socket.username,message:message});
//socket.to(socketId).emit('hey', 'I just met you');
//It's the same command as for rooms, that's because:
//"For your convenience, each socket automatically joins a room identified by this id."
});
socket.on('typing',function(username){
socket.broadcast.emit('typing', username);
});
socket.on('stopped-typing',function(){
socket.broadcast.emit('stopped-typing');
});
});
server.listen(8080);
My code: index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Real-time Super Chat!</title>
<style>
#zone_chat strong {
color: white;
background-color: black;
padding: 2px;
}
#writeup {
font-size:9px;
color: gray;
margin: 5px 0px;
}
</style>
</head>
<body>
<h4>Real-time Super Chat!</h4>
<form action="/" method="post" id="chat_form" autocomplete="off">
<input type="text" name="message" id="message" placeholder="Your message..." size="50" />
<input type="submit" id="send_message" value="Send" />
<input type="hidden" id="support_socket_id" value=""/>
<div id="writeup"></div>
</form>
<section id="chat_zone">
</section>
<script
src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
// Connecting to socket.io
var socket = io.connect('http://localhost:8080');
var socketConnected = false;
// The username is requested, sent to the server and displayed in the title
var username = prompt('What\'s your username?');
//var username = 'Deepak';
socket.emit('new_client', username);
document.title = username + ' - ' + document.title;
socket.on('connect', function() {
socketConnected = true;
console.log('Connected! ID: ' + socket.id);
});
socket.on('client_connected', function(data){
$('#support_socket_id').val(data.support_socket_id);
});
socket.on('disconnect', function() {
socketConnected = false;
console.log('Disconnected!');
});
// When a message is received it's inserted in the page
socket.on('message', function(data) {
insertMessage(data.username, data.message)
});
// When a new client connects, the information is displayed
// socket.on('new_client', function(username) {
// $('#chat_zone').prepend('<p><em>' + username + ' has joined the chat!</em></p>');
// });
socket.on('typing',function(username){
$('#writeup').html(username + ' is typing...');
});
socket.on('stopped-typing',function(){
$('#writeup').html('');
});
$(document).ready(function () {
$('#message').on('focus',function(){
socket.emit('typing',username);
});
$('#message').on('blur',function(){
socket.emit('stopped-typing',username);
});
// When the form is sent, the message is sent and displayed on the page
$('#chat_form').submit(function () {
var message = $('#message').val();
var support_socket_id = $('#support_socket_id').val();
socket.emit('message', { message:message, support_socket_id:support_socket_id }); // Sends the message to the others
insertMessage(username, message); // Also displays the message on our page
$('#message').val('').focus(); // Empties the chat form and puts the focus back on it
return false; // Blocks 'classic' sending of the form
});
});
// Adds a message to the page
function insertMessage(username, message) {
$('#chat_zone').prepend('<p><strong>' + username + '</strong> ' + message + '</p>');
}
</script>
</body>
</html>
You should create node modules in order to maintain the code structure. For example create routes folder and put all the route in there and separate them like users.js is a route file to cater routing related to user. As you are using socket.io you can create a separate module for socket.io functions and use them in your routes.
This link might help you: https://nodejs.org/api/modules.html
I hope it helps.
App Structure: routes, enter image description here
Related
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)
I'm following this tutorial to make a basic socket.io chatroom connected to mongodb. Here is the code:
const mongo = require('mongodb').MongoClient;
const client = require('socket.io').listen(3000).sockets;
// Connect to mongo
mongo.connect('mongodb://127.0.0.1/mongochat', function(err, db){
if(err){
throw err;
}
console.log('MongoDB connected...');
// Connect to Socket.io
client.on('connection', function(socket){
let chat = db.collection('chats');
// Create function to send status
sendStatus = function(s){
socket.emit('status', s);
}
// Get chats from mongo collection
chat.find().limit(100).sort({_id:1}).toArray(function(err, res){
if(err){
throw err;
}
// Emit the messages
socket.emit('output', res);
});
// Handle input events
socket.on('input', function(data){
let name = data.name;
let message = data.message;
// Check for name and message
if(name == '' || message == ''){
// Send error status
sendStatus('Please enter a name and message');
} else {
// Insert message
chat.insert({name: name, message: message}, function(){
client.emit('output', [data]);
// Send status object
sendStatus({
message: 'Message sent',
clear: true
});
});
}
});
// Handle clear
socket.on('clear', function(data){
// Remove all chats from collection
chat.remove({}, function(){
// Emit cleared
socket.emit('cleared');
});
});
});
});
The code runs and I get
(node:31737) DeprecationWarning: current URL string parser is
deprecated, and will be removed in a future version. To use the new
parser, pass option { useNewUrlParser: true } to MongoClient.connect.
MongoDB connected...
But when I try to get the html page in browser I get This 127.0.0.1 page can’t be found instead.
Here is the index.html page on the same folder as server.js that is supposed to be rendered but it does not:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<title>MongoChat</title>
<style>
#messages{height:300px;}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3 col-sm-12">
<h1 class="text-center">
MongoChat
<button id="clear" class="btn btn-danger">Clear</button>
</h1>
<div id="status"></div>
<div id="chat">
<input type="text" id="username" class="form-control" placeholder="Enter name...">
<br>
<div class="card">
<div id="messages" class="card-block">
</div>
</div>
<br>
<textarea id="textarea" class="form-control" placeholder="Enter message..."></textarea>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
<script>
(function(){
var element = function(id){
return document.getElementById(id);
}
// Get Elements
var status = element('status');
var messages = element('messages');
var textarea = element('textarea');
var username = element('username');
var clearBtn = element('clear');
// Set default status
var statusDefault = status.textContent;
var setStatus = function(s){
// Set status
status.textContent = s;
if(s !== statusDefault){
var delay = setTimeout(function(){
setStatus(statusDefault);
}, 4000);
}
}
// Connect to socket.io
var socket = io.connect('http://127.0.0.1:4000');
// Check for connection
if(socket !== undefined){
console.log('Connected to socket...');
// Handle Output
socket.on('output', function(data){
//console.log(data);
if(data.length){
for(var x = 0;x < data.length;x++){
// Build out message div
var message = document.createElement('div');
message.setAttribute('class', 'chat-message');
message.textContent = data[x].name+": "+data[x].message;
messages.appendChild(message);
messages.insertBefore(message, messages.firstChild);
}
}
});
// Get Status From Server
socket.on('status', function(data){
// get message status
setStatus((typeof data === 'object')? data.message : data);
// If status is clear, clear text
if(data.clear){
textarea.value = '';
}
});
// Handle Input
textarea.addEventListener('keydown', function(event){
if(event.which === 13 && event.shiftKey == false){
// Emit to server input
socket.emit('input', {
name:username.value,
message:textarea.value
});
event.preventDefault();
}
})
// Handle Chat Clear
clearBtn.addEventListener('click', function(){
socket.emit('clear');
});
// Clear Message
socket.on('cleared', function(){
messages.textContent = '';
});
}
})();
</script>
</body>
</html>
mongodb driver "mongodb": "^3.1.0-beta4",
mongodb database version v3.2.20
"socket.io": "^2.1.1"`
This problem bugs me for hours and I have no clues. So apprecite your help.
I have put together a video chat app using socket.io, webrtc, nodejs from this online tutorial from github but now I am getting errors when converting it to be used over https:
Request URL:https://telemed.caduceususa.com/socket.io/?EIO=3&transport=polling&t=1479396416422-0
Request Method:GET
Status Code:404 Not Found
Remote Address:10.9.2.169:443
Other errors I have gotten in this process are as follows:
When I try to declare a different PORT I get - ERR_SSL_PROTOCOL_ERROR,
When I try to declare port 80 or 8080 i get: ERR_CONNECTION_TIMED_OUT
Something is going wrong on this line inside socket.io.js:
xhr.send(this.data);
I am running a node.js server on Windows Server 2012 and I have set up IIS to serve up the server on PORT 80. I have created the subdomain https://telemed.caduceususa.com in DNS and have purchased a trusted SSL cert to run the site over HTTPS.
Here is the excerpt of code from the dev tools that contains the above line that is causing the error as well as my other code:
/**
* Creates the XHR object and sends the request.
*
* #api private
*/
Request.prototype.create = function(){
var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR };
// SSL options for Node.js client
opts.pfx = this.pfx;
opts.key = this.key;
opts.passphrase = this.passphrase;
opts.cert = this.cert;
opts.ca = this.ca;
opts.ciphers = this.ciphers;
opts.rejectUnauthorized = this.rejectUnauthorized;
var xhr = this.xhr = new XMLHttpRequest(opts);
var self = this;
try {
debug('xhr open %s: %s', this.method, this.uri);
xhr.open(this.method, this.uri, this.async);
if (this.supportsBinary) {
// This has to be done after open because Firefox is stupid
// https://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension
xhr.responseType = 'arraybuffer';
}
if ('POST' == this.method) {
try {
if (this.isBinary) {
xhr.setRequestHeader('Content-type', 'application/octet-stream');
} else {
xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
}
} catch (e) {}
}
// ie6 check
if ('withCredentials' in xhr) {
xhr.withCredentials = true;
}
if (this.hasXDR()) {
xhr.onload = function(){
self.onLoad();
};
xhr.onerror = function(){
self.onError(xhr.responseText);
};
} else {
xhr.onreadystatechange = function(){
if (4 != xhr.readyState) return;
if (200 == xhr.status || 1223 == xhr.status) {
self.onLoad();
} else {
// make sure the `error` event handler that's user-set
// does not throw in the same tick and gets caught here
setTimeout(function(){
self.onError(xhr.status);
}, 0);
}
};
}
debug('xhr data %s', this.data);
xhr.send(this.data);
}
Here is the server.js file:
var fs = require('fs');
var hskey = fs.readFileSync('ssl/telemed_internal_server.key');
var hscert = fs.readFileSync('ssl/telemed_internal_cert.pem');
var ca = fs.readFileSync('ssl/telemed_internal_key.pem');
var credentials = {
ca: ca,
key: hskey,
cert: hscert
};
var static = require('node-static');
var https = require('https');
var util = require('util');
var file = new(static.Server)();
var app = https.createServer(credentials, function (req, res) {
file.serve(req, res);
}).listen(process.env.PORT || 80);
var io = require('socket.io').listen(app);
io.sockets.on('connection', function (socket){
// convenience function to log server messages on the client
function log(){
var array = [">>> Message from server: "];
for (var i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}
// when receive sdp, broadcast sdp to other user
socket.on('sdp', function(data){
console.log('Received SDP from ' + socket.id);
socket.to(data.room).emit('sdp received', data.sdp);
});
// when receive ice candidate, broadcast sdp to other user
socket.on('ice candidate', function(data){
console.log('Received ICE candidate from ' + socket.id + ' ' + data.candidate);
socket.to(data.room).emit('ice candidate received', data.candidate);
});
socket.on('message', function (message) {
log('Got message:', message);
// for a real app, would be room only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', function (room) {
// join room
var existingRoom = io.sockets.adapter.rooms[room];
var clients = [];
if(existingRoom){
clients = Object.keys(existingRoom);
}
if(clients.length == 0){
socket.join(room);
io.to(room).emit('empty', room);
}
else if(clients.length == 1){
socket.join(room);
socket.to(room).emit('joined', room, clients.length + 1);
}
// only allow 2 users max per room
else{
socket.emit('full', room);
}
});
socket.on('error', function(error){
console.error(error);
})
});
Here is the main.js (config) file:
//my signalling server
var serverIP = "https://telemed.caduceususa.com/";
// RTCPeerConnection Options
var server = {
// Uses Google's STUN server
iceServers: [{
"url": "stun:stun4.l.google.com:19302"
},
{
url: 'turn:numb.viagenie.ca',
credential: 'muazkh',
username: 'webrtc#live.com'
}]
};
// various other development IPs
// var serverIP = "https://192.168.43.241:2013";
// var serverIP = "https://10.0.11.196:2013";
var localPeerConnection, signallingServer;
var btnSend = document.getElementById('btn-send');
var btnVideoStop = document.getElementById('btn-video-stop');
var btnVideoStart = document.getElementById('btn-video-start');
var btnVideoJoin = document.getElementById('btn-video-join');
var localVideo = document.getElementById('local-video');
var remoteVideo = document.getElementById('remote-video');
var inputRoomName = document.getElementById('room-name');
var localStream, localIsCaller;
btnVideoStop.onclick = function(e) {
e.preventDefault();
// stop video stream
if (localStream != null) {
localStream.stop();
}
// kill all connections
if (localPeerConnection != null) {
localPeerConnection.removeStream(localStream);
localPeerConnection.close();
signallingServer.close();
localVideo.src = "";
remoteVideo.src = "";
}
btnVideoStart.disabled = false;
btnVideoJoin.disabled = false;
btnVideoStop.disabled = true;
}
btnVideoStart.onclick = function(e) {
e.preventDefault();
// is starting the call
localIsCaller = true;
initConnection();
}
btnVideoJoin.onclick = function(e) {
e.preventDefault();
// just joining a call, not offering
localIsCaller = false;
initConnection();
}
function initConnection() {
var room = inputRoomName.value;
if (room == undefined || room.length <= 0) {
alert('Please enter room name');
return;
}
// start connection!
connect(room);
btnVideoStart.disabled = true;
btnVideoJoin.disabled = true;
btnVideoStop.disabled = false;
}
// WEBRTC STUFF STARTS HERE
// Set objects as most are currently prefixed
window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection || window.msRTCPeerConnection;
window.RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription ||
window.webkitRTCSessionDescription || window.msRTCSessionDescription;
navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia ||
navigator.webkitGetUserMedia || navigator.msGetUserMedia;
window.SignallingServer = window.SignallingServer;
var sdpConstraints = {
optional: [],
mandatory: {
OfferToReceiveVideo: true,
}
}
function connect(room) {
// create peer connection
localPeerConnection = new RTCPeerConnection(server);
// create local data channel, send it to remote
navigator.getUserMedia({
video: true,
audio: true
}, function(stream) {
// get and save local stream
trace('Got stream, saving it now and starting RTC conn');
// must add before calling setRemoteDescription() because then
// it triggers 'addstream' event
localPeerConnection.addStream(stream);
localStream = stream;
// show local video
localVideo.src = window.URL.createObjectURL(stream);
// can start once have gotten local video
establishRTCConnection(room);
}, errorHandler)
}
function establishRTCConnection(room) {
// create signalling server
signallingServer = new SignallingServer(room, serverIP);
signallingServer.connect();
// a remote peer has joined room, initiate sdp exchange
signallingServer.onGuestJoined = function() {
trace('guest joined!')
// set local description and send to remote
localPeerConnection.createOffer(function(sessionDescription) {
trace('set local session desc with offer');
localPeerConnection.setLocalDescription(sessionDescription);
// send local sdp to remote
signallingServer.sendSDP(sessionDescription);
});
}
// got sdp from remote
signallingServer.onReceiveSdp = function(sdp) {
// get stream again
localPeerConnection.addStream(localStream);
trace(localStream)
// if local was the caller, set remote desc
if (localIsCaller) {
trace('is caller');
trace('set remote session desc with answer');
localPeerConnection.setRemoteDescription(new RTCSessionDescription(
sdp));
}
// if local is joining a call, set remote sdp and create answer
else {
trace('set remote session desc with offer');
localPeerConnection.setRemoteDescription(new RTCSessionDescription(
sdp), function() {
trace('make answer')
localPeerConnection.createAnswer(function(
sessionDescription) {
// set local description
trace('set local session desc with answer');
localPeerConnection.setLocalDescription(
sessionDescription);
// send local sdp to remote too
signallingServer.sendSDP(sessionDescription);
});
});
}
}
// when received ICE candidate
signallingServer.onReceiveICECandidate = function(candidate) {
trace('Set remote ice candidate');
localPeerConnection.addIceCandidate(new RTCIceCandidate(candidate));
}
// when room is full, alert user
signallingServer.onRoomFull = function(room) {
window.alert('Room "' + room +
'"" is full! Please join or create another room');
}
// get ice candidates and send them over
// wont get called unless SDP has been exchanged
localPeerConnection.onicecandidate = function(event) {
if (event.candidate) {
//!!! send ice candidate over via signalling channel
trace("Sending candidate");
signallingServer.sendICECandidate(event.candidate);
}
}
// when stream is added to connection, put it in video src
localPeerConnection.onaddstream = function(data) {
remoteVideo.src = window.URL.createObjectURL(data.stream);
}
}
function errorHandler(error) {
console.error('Something went wrong!');
console.error(error);
}
function trace(text) {
console.info(text);
}
Here is the signalling server:
function trace(text){
console.info(text);
}
// Connects to signalling server with given room and IP
// has methods to exchange SDP and ICE candidates
var SignallingServer = function(room, socketServer){
this.room = room;
this.socket = io.connect(socketServer);
this.socket.on('full', function (room){
trace('Room ' + room + ' is full');
this.onRoomFull(room);
}.bind(this));
this.socket.on('empty', function (room){
this.isInitiator = true;
trace('Room ' + room + ' is empty');
});
this.socket.on('join', function (room){
trace('Making request to join room ' + room);
});
this.socket.on('joined', function (room, numClients){
trace('New user has joined ' + room);
trace('Room has ' + numClients + ' clients');
//ask host to initiate sdp transfer
this.onGuestJoined();
}.bind(this));
this.socket.on('sdp received', function(sdp){
trace('Received SDP ');
trace(sdp);
this.onReceiveSdp(sdp);
}.bind(this));
this.socket.on('ice candidate received', function(candidate){
trace('Received ICE candidate ');
trace(candidate);
this.onReceiveICECandidate(candidate);
}.bind(this));
this.socket.on('log', function (array){
console.log.apply(console, array);
});
}
SignallingServer.prototype = {
connect: function(){
if (this.room !== '') {
trace('Joining room ' + this.room);
this.socket.emit('create or join', this.room);
}
},
close: function(){
trace('Disconnecting')
this.socket.disconnect();
},
sendSDP: function(sdp){
trace('sending sdp')
this.socket.emit('sdp', {
room: this.room,
sdp: sdp
});
},
sendICECandidate: function(candidate){
trace('sending ice candidate');
this.socket.emit('ice candidate', {
room: this.room,
candidate: candidate
});
},
onReceiveSdp: function(sdp){
trace('Placeholder function: Received SDP')
},
onGuestJoined: function(){
trace('Placeholder function: Guest joined room')
},
onReceiveICECandidate: function(candidate){
trace('Placeholder function: Received ICE candidate')
},
onRoomFull: function(room){
trace('Placeholder function: Room is full!');
}
}
window.SignallingServer = SignallingServer;
AND FINALLY THE HTML (CAN SOMEONE ALSO EXPLAIN WHAT LIVERELOAD.JS IS?):
<!doctype html>
<!--[if lt IE 7]>
<html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="">
<![endif]-->
<!--[if IE 7]>
<html class="no-js lt-ie9 lt-ie8" lang="">
<![endif]-->
<!--[if IE 8]>
<html class="no-js lt-ie9" lang="">
<![endif]-->
<!--[if gt IE 8]>
<!-->
<html class="no-js" lang="">
<!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/bootstrap.min.css">
<style>
body {
padding-top: 50px;
padding-bottom: 20px;
}
</style>
<link rel="stylesheet" href="css/bootstrap-theme.min.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.8.3-respond-1.4.2.min.js"></script>
</head>
<body>
<!--[if lt IE 8]>
<p class="browserupgrade">
You are using an <strong>outdated</strong>
browser. Please
upgrade your browser
to improve your experience.
</p>
<![endif]-->
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">WebRTC Video Chat</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<!-- chatroom name form -->
<form class="navbar-form navbar-right form-inline">
<div class="form-group">
<input class="form-control" type="text" id="room-name" placeholder="Room name"/>
</div>
<button class="btn btn-primary" id="btn-video-start">Start</button>
<button class="btn btn-default" id="btn-video-join">Join</button>
<button class="btn btn-default" disabled id="btn-video-stop">Stop</button>
</form>
</div>
<!--/.navbar-collapse --> </div>
</nav>
<div class="container main">
<div class="row videos">
<div class="remote-video">
<video width="280" height="250" autoplay id="remote-video"></video>
</div>
<div class="local-video">
<video width="280" height="250" autoplay id="local-video" muted></video>
</div>
</div>
</div>
</div>
<!-- /container -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.11.2.min.js"><\/script>')</script>
<script src="js/vendor/bootstrap.min.js"></script>
<script src="js/vendor/socket.io.js"></script>
<script src="js/main.js"></script>
<script src="js/signalling.js"></script>
<script src="//localhost:9010/livereload.js"></script>
</body>
</html>
have been trying to create a chat app with node.js and socket.io, but have been stuck for a while and heres why, i succeeded in implementing sending of private messages between connected users by clicking on the username of the person u want to chat privately with, but the problem i now face is that, for example let say there are 3 connected users in the chat app(say joy, grace,shanel) when joy decides to chat with grace, the chat app handles this quite well,but if joy decides to chat with shanel after first chatting with grace, joy's private messages that are meant for shanel ends up being sent to grace, (i.e both grace and shanel recieves this private message for grace), this is the problem have been facing. my codes are below, sorry for the essay, i want anyone trying to help understand my situation :)
server.js code
var express = require('express');
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var usernames={};
var sockets = {};
var names= {};
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res){
res.sendfile('index.html');
});
io.on('connection', function(socket){
socket.on('send_msg', function(msg){
console.log('a user connected');
io.emit('chat message', msg);
//console.log(msg);
});
socket.on('new_user',function(user){
console.log('new user:'+user);
names[socket.id] = user;
socket.nickname= user;
usernames[socket.nickname] = socket;
sockets[user]=socket.id;
socket.emit('update_personal', "you are now online");
io.emit('update_users',names);
});
socket.on('disconnect', function(){
io.emit('update_personal', usernames[socket.id]+' is now offline');
//delete usernames[socket.id];
delete names[socket.id];
delete usernames[socket.nickname];
// io.emit('update_users',usernames,usernames[socket.id]);
io.emit('update_users',names);
//console.log(usernames+'specific user id'+usernames[user]);
});
socket.on('private_msg', function(msg,recipient,sender){
console.log('you are trying to send '+msg+' to '+recipient+ ' from '+sender);
var id = sockets[recipient];
console.log(sockets[recipient]);
io.to(id).emit('received_p_msg', msg,sender,recipient);
recipient = '';
console.log('value in recipient:'+recipient);
});
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
client.html
<!doctype html>
<html>
<head>
<title>my chat app</title>
<!------------------
<style>
<!-----------
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
#chat_msg{
}
</style><!-------!>
</head>
<body>
<!---username login here----->
<div id="login">
<form id="chat_msg">
<label>Enter Username:</label>
<input type="text" id="username"/>
<input type="button" id="join" value="Create Username"/>
</form>
</div>
<div>
<div id="online_users"><li>List of online users:</li></div>
</div>
<!---public room chat begins here----->
<div id="chat" style="display: none;">
<ul id="messages"></ul>
<form action="">
<input id="msg" autocomplete="off" />
<button id="send" >Send</button>
</form>
</div>
<!---private chat begins here----->
<div id="private_chat" style="display: none;">
<p id="private_user">Private chat with: </p>
<div id="private_msg"></div>
<form action="">
<input id="p_msg" autocomplete="off" />
<button id="p_send" >Send private msg</button>
</form>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="jquery-2.1.0.js"></script>
<script>
$(document).ready(function(){
var socket = io('http://192.168.173.1:3000/');
$('#chat').hide();
$('#username').focus();
$('form').submit(function(e){
e.preventDefault();
});
var username = $('#username').val();
$('#join').click(function(){
var username = $('#username').val();
console.log('entered username '+username);
if(username !=''){
socket.emit('new_user', username);
$('#login').detach();
$('#chat').show();
$('#msg').focus();
}
});
$('#send').click(function(){
socket.emit('send_msg', $('#msg').val());
$('#msg').val('');
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
socket.on('update_personal', function(status){
$('#messages').append($('<li>').text(status));
});
socket.on('update_users', function(names){
console.log(names);
if(true) {
$("#online_users").empty();
$.each(names, function(clientid, name) {
$('#online_users').append("<li><a href='#' id='"+name+"' name='"+name+"' class='private'> " + name + "</a></li>");
});
// $('#online_users').html("<li><a href='#' id='"+name+"' name='"+name+"' class='private'> " + name + "</a></li><br/>");
$('a.private').click(function(){
$('#private_chat').hide();
$('#private_chat').show();
var sender = username;
var recipient = $(this).attr('name');
console.log('name gotten is:'+recipient);
$('p#private_user').html('private chat with :'+ recipient);
$('#p_send').click(function(){
msg = $('#p_msg').val();
if(msg!=''){
recipient=recipient;
socket.emit('private_msg', msg,recipient,sender); // msg from sender, username of the sender, username of recipient
$('#p_msg').val('');
}else{$('#p_msg').val('please enter a valid msg');}
});
});
}
});
socket.on('received_p_msg', function(msg,sender,recipient){
$('#private_chat').show();
console.log('received privatemsg: '+msg);
$('#private_user').html('privatE chat with : '+ sender);
$('#private_msg').append($('<div>').html(msg+'</div>'));
//to continue chat after receiving initial private msg
$('#p_send').click(function(){
msg = $('#p_msg').val();
if(msg!=''){
socket.emit('private_msg', msg,sender,recipient); // msg from sender, username of the sender, username of recipient
$('#p_msg').val('');
}else{$('#p_msg').val('please enter a valid msg');}
$('#p_msg').val('');
});
});
socket.on("disconnect", function(){
$("#msgs").append("The server is not available");
});
});
</script>
</body>
</html>
Regarding your issue I noticed that if you want to open multiple private chats you can't because there is only one username and so it isn't possible to choose more than one client to send the chat message to. You need to extend the username variable into an array of usernames for example.
I found a possible bug in your client.html file, in which username was taken from $("#username").val() but it was undefined. I suggest you this fix if I understood correctly your code (3 different lines):
// the line below differs
var username = null;
var socket = io('http://192.168.173.1:3000/');
$('#chat').hide();
$('#username').focus();
$('form').submit(function(e){
e.preventDefault();
});
// the line below differs
//var username = $('#username').val();
$('#join').click(function{
// the line below differs
username = $('#username').val();
console.log('entered username '+username);
if(username !=''){
socket.emit('new_user', username);
$('#login').detach();
$('#chat').show();
$('#msg').focus();
}
});
Since you want the messages of two parties to remain private between them, I suggest that you create a room for each of the two parties. As it stands now it think you're sending / broadcasting your event to all parties in the room which contains all the users.
I thought the whole point of socket.io was to not have to worry about modern browsers? lol
Anyway, I am new to socket programming. I have a small app that simply mimics mouse movements.
You open several browsers and when you move the mouse, your actions are recorded in the other browsers. It moves a small square. Kinda cool. However, when I open it on my iPad (iOS6) nothing! Sockets isn't connecting. I even put an alert message in the connect event and nothing.
Works in IE, FF and Chrome just fine on my laptop. The only difference is that my dev machine uses localhost while the iPad uses my machine's IP. However, when I connect to my local IP on my laptop, it still works. Just not in Safari/iPad.
Here is my server.
var app = require('http').createServer(handler),
io = require('socket.io').listen(app),
fs = require('fs');
app.listen(80);
function handler(req, res) {
var file = __dirname + '/public/index.html';
fs.readFile(file,
function(err, data) {
if(err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
}
);
}
var rooms = ['abc', 'test1'];
var sockets = [];
io.sockets.on('connection', function(socket) {
sockets.push(socket);
socket.on('m', function(data) {
socket.broadcast.to(socket.room).emit('relay', {msg: 'MouseX: ' + data.x + ' MouseY: ' + data.y, x: data.x, y: data.y});
});
socket.on('join', function(room) {
socket.join(room);
socket.emit('updateStatus', {msg: 'Joined room ' + room});
console.log('Joined room ' + room);
});
});
Here is my client:
<!doctype html>
<html>
<head>
<style>
body {
padding: 40px;
}
#cursor {
background:white;
border:1px solid black;
color: white;
display: block;
height:24px;
padding:6px;
position:absolute;
width:24px;
z-index:20;
}
</style>
</head>
<body>
<input id='msg' type='text' size='100' /><br />
<input id='box' type='text' size='100' />
<div id='cursor'></div>
<script src='/socket.io/lib/socket.io.js'></script>
<script>
var socket = io.connect('http://localhost');
var b = document.getElementById('box');
var m = document.getElementById('msg');
var c = document.getElementById('cursor');
// join custom room
socket.on('connect', function() {
socket.emit('join', 'abc');
});
// update status messages from server
socket.on('updateStatus', function(data) {
m.setAttribute('value', data.msg);
});
socket.on('relay', function(data) {
b.setAttribute('value', data.msg);
c.style.left = parseInt(data.x) + 'px';
c.style.top = parseInt(data.y) + 'px';
});
document.onmousemove = function(event) {
event = event || window.event;
socket.emit('m', {x: event.clientX, y: event.clientY});
}
</script>
</body>
</html>
Localhost is local to the machine. You're IP should use a ip address or domain name:
something like:
io.connect('192.168.1.110');
or io.connect('test.myapp.com');