Showing posts with label websockets. Show all posts
Showing posts with label websockets. Show all posts

Monday, March 21, 2016

Amphibian.com's got Web Sockets

Today's comic might look like a simple joke about planting light bulbs, but it is really so much more. Well, actually it's only a little bit more. But still more.

The lamp post in the third frame can be turned on and off by clicking on it. But it doesn't just go on and off for you the clicker; it goes on and off for everyone looking at the comic. Try it. Go to Amphibian.com and look at the lamp. Call a friend on the other side of the world and have them also go to Amphibian.com and look at the lamp. Click on the lamp. You'll both see it toggle state at the same time.

I finally got around to integrating Web Sockets via Socket.io into the site. The state of the lamp is stored on the server and clicks emit "lamp-toggle" events from the clients. When a toggle event is received on the server, the state of the lamp switches and the new state (on or off) is broadcast out to all the clients. The clients show or hide the "glow" effect accordingly.

Server Code:

var lampOn = true;

io.on("connection", function (socket) {

    socket.on("lamp-toggle", function(data) {

        if (lampOn) {
            io.emit("lamp-off");
        } else {
            io.emit("lamp-on");
        }

        lampOn = !lampOn;

    });

});

Client Code:

var socket = io("http://amphibian.com");

socket.on("lamp-off", function(d) { 
    $("#glow").hide();
});

socket.on("lamp-on", function(d) {
    $("#glow").show();
});

$("#lamp").click(function() {
    socket.emit("lamp-toggle");
});

On the server side, I had a little trouble with the fact that I have Node HTTP servers for both secure and insecure web traffic in the application. I wanted the same Socket.io instance to service both. Fortunately, it is easy to attach a Socket.io server to multiple HTTP servers. In the code snippet below, I call attach on an existing Socket.io instance to bind it to a second HTTP server (line 17).

var app = express();

var server = http.createServer(app).listen(3000, function() {
    console.log('listening on port %d', server.address().port);
});

// create a Socket.io server attached to the HTTP server just created
var io = require('socket.io')(server);

if (ssl) {

    var secureServer = https.createServer(sslOptions, app).listen(4443, function() {
        console.log('listening securely on port %d', secureServer.address().port);
    });

    // attach the Socket.io server to the secure HTTP server as well
    io.attach(secureServer);

}

Back in the client, I just had to be smart about which URL to connect to. I never got Web Sockets to work correctly with Nginx, so I'm just bypassing it when I make the connections back from the client. Nginx listens on ports 80 and 443 and proxies HTTP traffic to the Node application which actually listens on ports 3000 and 4443. When I set up Socket.io in the client, I use these ports directly. Browsers still allow Web Socket connections to different ports on the same server without raising cross-site scripting concerns. But you can't mix insecure HTTP and secure Web Sockets (or vice versa). Look at this update to the client code from above, where I examine the browser's location to determine what URL I should use for connecting:

var urlParts = window.location.href.split("/");
var cUrl = urlParts[0] + "//" + urlParts[2];
if (!window.location.port) {
    if (urlParts[0] !== "https:") {
        cUrl += ':3000';
    } else {
        cUrl += ':4443';
    }
}

var socket = io(cUrl);

// ... rest of client code from above ...

So go play around with that lamp! And feel free to read more comics while you're there.

Amphibian.com comic for 21 March 2016

Wednesday, March 11, 2015

Going to the Server?

In previous posts, I've talked quite a bit about how I use Websockets for sending data from the server to the browser. For asynchronous server-to-client "push" scenarios, it can't be beat. But what about client-to-server? Using classic AJAX to post data to the server is still prevalent, but you may not be surprised to learn that Websockets are more efficient for that use-case as well.

HTTP is expensive, even when it's going on asynchronously in the background of your pages. I wanted to see just how much more it costs in terms of time to send data to the server using a classic HTTP POST versus sending data over an open Websocket. I set up the following test.

First, I set up a Node application using Express and Socket.io.

var express = require("express");
var http = require("http");

var app = express();
var server = http.Server(app);
var io = require("socket.io")(server);

//------------ Socket.io stuff

var wFirstSeen = null;
var events = 0;

io.on("connection", function(socket) {
  
    socket.on("data", function(data) {

        if (!wFirstSeen) {
            wFirstSeen = new Date();
        }
        events++;

        if (events === 200) {
            console.log('websockets done in ' + ((new Date() - wFirstSeen)));
            events = 0;
            wFirstSeen = null;
        }

    });

});

//------------ web application route

var hFirstSeen = null;
var posts = 0;

app.post("/data", function(req, res, next) {

    if (!hFirstSeen) {
        hFirstSeen = new Date();
    }
    posts++;

    if (posts === 200) {
        console.log('http post done in ' + ((new Date() - hFirstSeen)));
        posts = 0;
        hFirstSeen = null;
    }

    res.sendStatus(200);

});

app.use(express.static('public'));

// ------------ start the listening

var srv = server.listen(3000, function() {
    console.log("listening on port %d", srv.address().port);
});

Then I made a test web page so my client could send data to the server.

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>WS vs HTTP</title>
</head>

<body>

  <p>Pushes data to the server.</p>
  
  <div id="wsStatus"></div>

  <div id="httpStatus"></div>

</body>

<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript" src="/test.js"></script>

</html>

Finally, here are the contents of my test.js file.

var sckt;
var data = {
    field1: "something",
    field2: 12,
    field3: false,
    field4: "whatnot"
}

function websocketTest() {

    $("#wsStatus").append("starting send");
    for (var x = 0; x < 200; x++) {
        sendWebsocketData();
    }
    $("#wsStatus").append("sends complete");

}

function ajaxTest() {

    $("#httpStatus").append("starting send");
    for (var x = 0; x < 200; x++) {
        sendAjaxData();
    }
    $("#httpStatus").append("sends complete");

}

function sendWebsocketData() {
    sckt.emit("data", data);
}

function sendAjaxData() {
    $.post("/data", data);
}

$(function() {

    sckt = io.connect();
    sckt.on("connect", function() {
        $("#wsStatus").append("socket connected");
    });

});

So my test was pretty simple. I send the same data object from the client to the server 200 times and print out how long it takes for the server to process. I have test methods set up for Websockets and HTTP post. After I bring up my test page in my browser and see that my websocket connects, I can execute the websocketTest function and the ajaxTest function. The server will print out the results, which are in milliseconds.

The results in my Eclipse console

The send via Websocket was quite a bit faster every time, which is what I had expected. Posts consistently took well over 200 ms, whereas the data could be transmitted by websocket in the 45 ms range. Definitely something to think about when designing a web application that will be sending a lot of data from the client to the server. Websockets are currently supported across most browsers, so using them in place of HTTP posts may be a viable option.

That's enough seriousness for today. In today's frog comic, I examine the push for self-driving automobiles.

Amphibian.com comic for 11 March 2015

Saturday, February 23, 2013

Node.js and Socket.io Allow Frogs to Play Together

It can be lonely out there for frogs. They need to interact with other frogs, and not just on Facebook. In the real world. But this isn't the real world, it's a virtual world that is supposed to be sorta like the real world. For frogs. Let's just say that if you want to connect multiple client browsers together to create some sort of online frog collaboration environment, there is no better way than with Node.js and Socket.io.

FFZ is where I play around with browser tech like WebSockets, Canvas, and HTML5 Audio. I like to make things with frogs in them. In this application, you can move a frog around a small world filled with trees, flowers, rocks, and streams. But it's even more fun when another person is on the site at the same time you are - you'll play in a collaborative environment. You will see their frog move in real-time and they'll see yours. It amuses my young children for hours. Just press "R" to ribbit.

How do I hook everybody's browser up together? Well first of all, don't expect it to work with Internet Explorer. Seriously. IE, how can you even show your face in public anymore? Chrome and Firefox work like browsers should. I'm talking WebSockets. Sure, you can do WebSockets the old-fashioned way...but Socket.io is a great utility that abstracts much of the complexity away from you. It's built for Node.js, the awesome server-side Javascript engine. Node allows you to hook up tons of clients together with very little overhead because of its event-driven IO model.

My FFZ "server" is a Node.js program that listens for events from the client browsers running the application and publishes events out to the clients as well. When one frog moves, it publishes its changed position to the server, which turns around the publishes it to all the other browsers so they can update the positions of that frog on their screens. And thanks to Socket.io, it's extremely simple.

Here's my Node server code:


var http = require("http");
var sockio = require("socket.io");

var frogs = [];

var io = sockio.listen(8080);
io.configure(function() {
    io.set('log level', 2);
    io.set('transports', [
      'websocket'
    ]);
});

io.sockets.on("connection", function(socket) {
      
    socket.on("frogmove", function(fdata) {
        socket.broadcast.emit("fm", fdata);
    });

    socket.on("objmove", function(odata) {
        socket.broadcast.emit('sm', odata);
    });
      
    socket.on("ribbit", function(fdata) {
        socket.broadcast.emit("rbbt", fdata);
    });
      
    socket.on("startup", function(msg) {
        console.log("frog " + msg.fid + " connected");
        socket.broadcast.emit("newfrog", msg.fid);
 socket.set("frog id", msg.fid);
 frogs.forEach(function(i) {
            socket.emit("newfrog", i);
 });
 frogs.push(msg.fid);
    });
      
    socket.on("disconnect", function() {
        socket.get("frog id", function(err, fid) {
            console.log("frog " + fid + " disconnected");
     socket.broadcast.emit("byefrog", fid);
         if (frogs.indexOf(fid) != -1) {
                frogs.splice(frogs.indexOf(fid), 1);
            }
 });
    });

});


So hopefully the first part of the code is fairly self-explanatory. In the configuration of the socket listener, I set the "transports" to be just "websocket" because I don't want it to automatically downgrade to Flash or long-polling. Those things are fine I suppose but I wanted to use FFZ to try out WebSockets. (I actually did try Flash and long-polling with FFZ. Flash is ok but long-polling just doesn't work with an application like this - there was just too much data being transmitted and the user experience was poor.)

The second block is where the real magic happens. On a "connection" event, we set up the event listeners for that socket. The first three events are very simple. When a socket, which represents a connection to a client, gets a "frogmove" event, for example, it just turns around and broadcasts it out again. The "broadcast" method sends a message to all known sockets except oneself - so the sender isn't going to get the message back but every other socket that Socket.io knows about will get it. This is how I handle the simple events - moving frogs and objects and making frogs ribbit.

The "startup" event gets a little more complicated. When a new client connects, they need to let all the other clients know to add them to their screens, but the new client also needs to know where all the existing frogs are so they can be added to their screen. So here I use the "socket.broadcast.emit" method again to tell everyone else about the new frog - and then I use "socket.emit" to send a "newfrog" event back to the socket that just joined. This is a bit of a trick because these aren't exactly new frogs (they existed before the new client joined) but the client will behave the same way as if a new frog joined - by adding the other frog(s) to the screen and tracking them for future updates. Finally, I add the new client id to the frogs array so the server knows about this frog in the future, at least until it disconnects.

That brings me to the "disconnect" event. Again, I use the broadcast to tell everyone else to stop displaying and tracking the frog that just left. I also then remove it from the frogs array.

So there you have it. A server in Node.js that keeps all the frogs playing nicely together. Easy peasy lemon squeezy. Now let's take a gander at the client code too. It's almost as simple.

The first thing to do in the client is to load the Socket.io code. But it serves itself! That's right, when Socket.io wants a sandwich it makes it and brings it to itself. You just source the JavaScript right from your Node.js server. So for example, if you look at the server code above you will notice that I'm running on port 8080 and I serve everything from amphibian.com. So I have this in my HTML:

<script type="text/javascript" src="http://www.amphibian.com:8080/socket.io/socket.io.js"></script>

Bam! You've got the Socket.io client now! By the way, you can actually serve the client manually if you need to for some obscure reason. See https://github.com/LearnBoost/socket.io-client

This is what I do to get the socket connected and set up the client's frog to publish events:


<script type="text/javascript">

sckt = io.connect("http://www.amphibian.com:8080");

sckt.on("connect", function() {

    console.log("socket connected");
    frog.bind('move', function(fdata) {
        // fdata will have the frog's id and position info
        sckt.emit("frogmove", fdata);
    });

    frog.bind('ribbit', function(fdata) {
        // fdata will have the frog's id and position info
        sckt.emit("ribbit", fdata);
    });

});

</script>


The "connect" call is fairly simple, you just give it the URL. Then you get up your connect event callback. When the "connect" event occurs, I bind some events on the client's frog to functions which will emit data over the socket. (I use MicroEvent.js to do this. You can read more about that here. It is awesome.) This works pretty much the same way as on the server side. Calling "emit" with an event name and some data sends that event+data to the server where (hopefully) there is a callback set up listening for that event. So in the code above, I emit the "frogmove" event and the "ribbit" event, both of which are listened for in the server code shown previously.

Remember that the server essentially rebroadcasts events from one client to all the other clients using custom event names. I'll just show you one here, the "rbbt" event, but the others are all very similar.


<script type="text/javascript">

sckt.on("rbbt", function(fdata) {
    for (var f = 0; f < otherfrogs.length; f++) {
        if (otherfrogs[f].uid == fdata.id) {
            otherfrogs[f].ribbit();
        }
    }
});

</script>

If you look at the server code and the first part of the client setup, you'll see that when a client's frog ribbits the event is published over the socket as the "ribbit" event. The server gets that and republishes to all other clients as a "rbbt" event. I removed the vowels. Servers don't like to broadcast vowels. No, that statement is completely false. Really I just wanted a slightly different event name so I could tell the client and server events apart. Anyway, this is an example of the client listening for the "rbbt" custom event. If this client gets such an event it means that some other client's frog is making a sound and so we should show that frog opening his mouth and play the sound as well. The data that comes in will have the other frog's id in it so we just look for which frog it should be and then call the "ribbit" method on that frog.

Couldn't be easier, right?

And there's even better news! What if you wanted to hook up your OUYA game to a Node.js server using Socket.io? I know I do! There is a Java client available for Socket.io that works on Android. So technically you could hook up web browsers, mobile phones, game consoles, and possibly even refrigerators all to the same server to share data in real-time. Yes, Node.js most assuredly rocks.


Saturday, November 6, 2010

Break the Request-Response Cycle! WebSockets are Coming!

Since the dawn of time, or at least since 1994, web developers have been forced to work within the confines of the request-response cycle. I've seen good developers be totally confused by the strange disconnected nature of the client and server. For over a decade now, we've been looking for ways to overcome this limitation to deliver web applications that are more like "regular" applications. Around the turn of the century, AJAX started to appear. Now the client could send and receive data from the server without having to interrupt the user with a page load. Great, but we still lacked a way for the server to initiate communication with the client. Comet, sometimes called Reverse-AJAX, seemed to be the answer in 2006. But long-polling HTTP requests are not perfect by any means, and the search continued for a better way. Could WebSockets be the ultimate solution to this problem? Let's give them a try and see how it works.

WebSockets (read the official spec here) are HTML5's new method of connecting the server with the client, via an "upgraded" HTTP connection. Basically, the client makes an HTTP request to the server and requests an upgrade to WebSockets. If the server agrees, the socket stays open and both the client and server can send information back and forth any time they want. In a compliant web browser a WebSocket API is available in JavaScript that makes this possible, much like the XMLHttpRequest API does for AJAX. So if you have Chrome, Safari, or Firefox 4, you're all set on the client side. But what about the server?

You'll need a web server that supports WebSockets in order to run an application that makes use of them. I wanted to find a way to use WebSockets with Tomcat, but had no luck. There is a project underway to add WebSockets support to Tomcat, but it is done via an add-on of what is essentially another server. I wanted something a little cleaner, so I went with Jetty 7.2. If you're going to serve a Java application with WebSockets I think Jetty is the way to go. Using Java's NIO, it has supported a highly efficient version of the Bayeux protocol (CometD) for a while now. WebSockets support is the logical next step, and it's nicely integrated. A WebSocketServlet class is available for you to extend which makes life easy. Here's a really simple one I made to test with.
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;

public class WebSocketTestServlet extends WebSocketServlet {

protected WebSocket doWebSocketConnect(HttpServletRequest request,
                                      String protocol) {
 return new TestWebSocket();
}

}
And here's the code for the TestWebSocket class:

import java.io.IOException;
import org.eclipse.jetty.websocket.WebSocket;

public class TestWebSocket implements WebSocket {

private Outbound ob;

public void onConnect(Outbound outbound) {
 this.ob = outbound;
 SocketStorage.getInstance().addSocket(this);
}

public void onDisconnect() {
 SocketStorage.getInstance().removeSocket(this);
}

public void onFragment(boolean arg0, byte arg1, byte[] arg2, int arg3,
  int arg4) {
 // ignore fragments for now
}

public void onMessage(byte arg0, String arg1) {

 System.out.println("--- received string \"" + arg1 + "\"");
 try {
  ob.sendMessage("thanks for the " + arg1);
 } catch (IOException e) {
  e.printStackTrace();
 }

}

public void onMessage(byte arg0, byte[] arg1, int arg2, int arg3) {
 // ignore byte arrays for now
}


}

That looks really simple, right? All you need to do is add a mapping for this servlet in your web.xml and you'll be able to connect via WebSockets to that URL. So how does this work? Well, if a request comes into the servlet with the right upgrade request headers, the request will be handed off to the doWebSocketConnect method instead of the regular doGet or doPost methods. Then it's up to you to return an instance of a class that implements the WebSocket interface. In the WebSocketServlet base class, Jetty will take care of holding on to that instance and making sure it gets it's data from the client. But remember, if you want to be able to send data asynchronously to those clients you need to keep track of that instance yourself somewhere. That's why you'll see the SocketStorage calls in onConnect and onDisconnect. SocketStorage is just a singleton I made that has a set of all the open WebSockets.

It should be obvious what most of the methods in TestWebSocket do. The Outbound instance that is passed in to the onConnect method is what you'll use to send data outbound to the client, so you need to hold on to it. The onMessage method is invoked whenver the client sends some data. In my example, I basically just echo back to the client whatever it sends me. If you were to add another method to TestWebSocket, you could easily expose the ability to send data to the client unrelated to what the client is sending to the server. Just call sendMessage on the Outbound instance and the data will find its way.

What about the client-side code? It's just as simple! Look at this JavaScript for the client:

     var ws;

  function connect() {

      ws = new WebSocket("ws://yourwebsite.com/path/to/websocket/servlet");
      ws.onopen = function() {
          console.log("connection openned");
      }
      ws.onmessage = function(m) {
          console.log(m.data);
      }
      ws.onclose = function() {
          console.log("connection closed");
      }

  }

  function sendMessage(msg) {
   ws.send(msg);
  }
Note the new protocol, "ws://", in the URL given to the WebSocket constructor. If you want secure WebSockets, "wss://" is also available. By default, the WebSocket ports are 80 and 443 (non-secure and secure, respectively) the same as HTTP.

So obviously my example here doesn't do much of anything. It was just a little test I put together to see how WebSockets work in real life. But now that we have this capability, what will we do with it? The common examples people always give are chat applications, stock alerts, things like that. But I'm thinking that there's a really unique idea out there just waiting to be built using new HTML5 technologies like this. I, for one, intend to use it to make some multi-player frog games.

2011 should be the year of the WebSocket. With Safari and Chrome already supporting them and Firefox 4 due out the beginning of the year, they'll be available in a huge percentage of clients. It looks like IE9 won't support them, but IE's market share will hopefully continue to decline. What I'd really like to see is some kind of Java standard approach for the server side. Jetty's implementation seems pretty good, so hopefully sometime next year Tomcat adds support for WebSockets in a similar fashion. I'd be happy to work on adding that if anyone asked me to...