Node.js and Web Sockets

A basic HTTP server:

// Use the http module: http://nodejs.org/api/http.html
var http = require('http');

// http://nodejs.org/api/http.html#http_event_request
function onRequest(req, res) {
	//req is an IncominMessage: http://nodejs.org/api/http.html#http_http_incomingmessage
	//res is a ServerResponse: http://nodejs.org/api/http.html#http_class_http_serverresponse
	res.writeHead(200, {'Content-Type': 'text/plain'});
	res.end('Hello World\n');
	
	// Send a log message to the console
	console.log("Got a request " + req.url);
}

// Call the createServer method, passing in an anonymous callback function that will be called when a request is made
var httpServer = http.createServer(onRequest);

// Tell that server to listen on port 8080
httpServer.listen(8080);  

console.log('Server listening on port 8080');		
		

To run, save this as "httpserver.js", upload it to your server and run it with:

node httpserver.js

A great write-up/explanation/tutorial for getting started with Node: http://www.nodebeginner.org/

Forever

Unfortunately, in the above example, the server will shutdown and stop running once you log out or stop it with cntrl-c. Forever is a node module that allows us to run a node server in the background.

Install the module:

npm -g install forver

Then run the server with it:

forever start httpserver.js

List running servers (running using forever)

forever list
which should output a list of running forever scripts
info:    Forever processes running
data:        uid  command             script    forever pid   logfile                 uptime       
data:    [0] GpWl /usr/local/bin/node server.js 16677   16679 /root/.forever/GpWl.log 0:0:0:14.683 
This shows one process running with the ID of GpWl (which is important if you want to shut it down). It's log file is here: /root/.forever/GpWl.log which is where any output that it generates will be written.

Stop it

forever stop ID
(see above for the ID)

More information: http://blog.nodejitsu.com/keep-a-nodejs-server-up-with-forever

Socket.io

Socket.io is a node module that makes working with WebSockets easier. It also transparently supports older methods such as AJAX in the event that the browser doesn't support Web Sockets.

Before we do any programming, we should install the module in the directory we are going to use

npm install socket.io

Here is a basic example that we can go through:

// HTTP Portion
var http = require('http');
var fs = require('fs'); // Using the filesystem module
var httpServer = http.createServer(requestHandler);
var url = require('url');
httpServer.listen(8080);

function requestHandler(req, res) {

	var parsedUrl = url.parse(req.url);
	console.log("The Request is: " + parsedUrl.pathname);
	
	// Read in the file they requested
	
	fs.readFile(__dirname + parsedUrl.pathname, 
		// Callback function for reading
		function (err, data) {
			// if there is an error
			if (err) {
				res.writeHead(500);
				return res.end('Error loading ' + parsedUrl.pathname);
			}
			// Otherwise, send the data, the contents of the file
			res.writeHead(200);
			res.end(data);
  		}
  	);
}

// WebSocket Portion
// WebSockets work with the HTTP server
var io = require('socket.io').listen(httpServer);

// Register a callback function to run when we have an individual connection
// This is run for each individual user that connects
io.sockets.on('connection', 
	// We are given a websocket object in our function
	function (socket) {
	
		console.log("We have a new client: " + socket.id);
	
		// When this user "send" from clientside javascript, we get a "message"
		// client side: socket.send("the message");  or socket.emit('message', "the message");
		socket.on('message', 
			// Run this function when a message is sent
			function (data) {
				console.log("message: " + data);
				
				// Call "broadcast" to send it to all clients (except sender), this is equal to
				// socket.broadcast.emit('message', data);
				socket.broadcast.send(data);
				
				// To all clients, on io.sockets instead
				// io.sockets.emit('message', "this goes to everyone");
			}
		);
		
		// When this user emits, client side: socket.emit('otherevent',some data);
		socket.on('otherevent', function(data) {
			// Data comes in as whatever was sent, including objects
			console.log("Received: 'otherevent' " + data);
		});
		
		
		socket.on('disconnect', function() {
			console.log("Client has disconnected");
		});
	}
);
		

Save this file as whatever.js and upload it to a new directory on your server.

Here is the corresponding index.html file that should be uploaded to the same directory. (Don't forget to replace the SERVER_NAME with your server's name or address.):

<html>
	<head>
		<script src="/socket.io/socket.io.js"></script>
		<script>
			var socket = io.connect('http://SERVER_NAME:8080/');
			
			socket.on('connect', function() {
				console.log("Connected");
			});

			// Receive a message
			socket.on('message', function(data) {
				console.log("Got: " + data);
				document.getElementById('messages').innerHTML += data;
			});

			// Receive from any event
			socket.on('news', function (data) {
				console.log(data);
			});
			
			var sendmessage = function() {
				var message = document.getElementById('message').value;
				console.log("Sending: " + message);
				
				// Send a messaage
				socket.send(message);
			};
			
			var sendother = function() {
				var othermessage = document.getElementById('message').value;
				console.log("sending: " + othermessage);
				
				// Send any kind of data with a custom event
				//socket.emit('otherevent',{ othermessage: othermessage });
				socket.emit('otherevent', othermessage);
			};
		</script>
	</head>
	<body>
 		<div id="messages">
			No Messages Yet
		</div>
		<div>
			<input type="text" id="message" name="message">
			<input type="button" value="message" onclick="sendmessage();">
			<input type="button" value="other" onclick="sendother();">
		</div>
	</body>
</html>
		

More documentation and examples: http://socket.io/ and https://github.com/Automattic/socket.io

Other Data

How about using this with the JavaScript Canvas.

Starting with the HTML. (Don't forget to replace the SERVER_NAME with your server's name or address.) Let's call it canvas_socket.html:

<html>
	<head>
		<script type="text/javascript" src="/socket.io/socket.io.js"></script>
		<script type="text/javascript">
		
			var socket = io.connect('http://SERVER_NAME:8080/');
			
			socket.on('connect', function() {
				console.log("Connected");
			});

			// Receive from any event
			socket.on('othermouse', function (data) {
				console.log(data.x + " " + data.y);
				odraw(data.x,data.y);
			});
			
			var sendmouse = function(xval, yval) {
				console.log("sendmouse: " + xval + " " + yval);
				socket.emit('othermouse',{ x: xval, y: yval });
			};

			///////////////
			
			var canvas;
			var context;

			var initcanvas = function() {
				canvas = document.getElementById('mycanvas');
				context = canvas.getContext('2d');
			
				context.fillStyle="#FF0000";
				context.fillRect(0,0,canvas.width,canvas.height);	
				
				canvas.addEventListener('mousemove', function(evt) {
					
					console.log("mousemove " + evt.clientX + " " + evt.clientY);
					
					//evt.clientX is x but in the entire window, not the canvas
					//evt.clientY is y
		
					// Get the canvas bounding rect
					var canvasRect = canvas.getBoundingClientRect(); 

					// Now calculate the mouse position values
					y = evt.clientY - canvasRect.top; // minus the starting point of the canvas rect
					x = evt.clientX - canvasRect.left;  // minus the starting point of the canvas rect on the x axis
				
					console.log("mousemove x:" + x + " y:" + y);

					sendmouse(x,y);
					draw(x,y);
				}, false);				
			};
				
			var px = 0;
			var py = 0;
	
			var draw = function(xval,yval) {
				
				console.log("draw " + xval + " " + yval);
				
				context.beginPath();
	
				// This is silly but it's what we have to do to get a random hex string
				context.strokeStyle='#000000';
	
				context.moveTo(px,py);
				context.lineTo(xval,yval);

				context.stroke();
				
				px = xval;
				py = yval;
			};

			var opx = 0;
			var opy = 0;
	
			var odraw = function(xval,yval) {
				
				console.log("draw " + xval + " " + yval);
				
				context.beginPath();
	
				// This is silly but it's what we have to do to get a random hex string
				context.strokeStyle='#000000';
	
				context.moveTo(opx,opy);
				context.lineTo(xval,yval);

				context.stroke();
				
				opx = xval;
				opy = yval;
			};
	
		</script>	
	</head>
	<body onload="initcanvas();">
		<canvas width="600" height="600" id="mycanvas" />
	</body>
</html>
		

Now the javascript server portion:

// HTTP Portion
var http = require('http');
var fs = require('fs'); // Using the filesystem module
var httpServer = http.createServer(requestHandler);
var url = require('url');
httpServer.listen(8080);

function requestHandler(req, res) {

	var parsedUrl = url.parse(req.url);
	console.log("The Request is: " + parsedUrl.pathname);
		
	fs.readFile(__dirname + parsedUrl.pathname, 
		// Callback function for reading
		function (err, data) {
			// if there is an error
			if (err) {
				res.writeHead(500);
				return res.end('Error loading ' + parsedUrl.pathname);
			}
			// Otherwise, send the data, the contents of the file
			res.writeHead(200);
			res.end(data);
  		}
  	);
}


// WebSocket Portion
// WebSockets work with the HTTP server
var io = require('socket.io').listen(httpServer);

// Register a callback function to run when we have an individual connection
// This is run for each individual user that connects
io.sockets.on('connection', 
	// We are given a websocket object in our function
	function (socket) {
	
		console.log("We have a new client: " + socket.id);
		
		// When this user emits, client side: socket.emit('otherevent',some data);
		socket.on('othermouse', function(data) {
			// Data comes in as whatever was sent, including objects
			console.log("Received: 'othermouse' " + data.x + " " + data.y);
			
			// Send it to all of the clients
			socket.broadcast.emit('othermouse', data);
		});
		
		
		socket.on('disconnect', function() {
			console.log("Client has disconnected " + socket.id);
		});
	}
);