1

I am trying to develop a very simple image server with NodeJS & SocketIO. A project I am working on requires me to load several hundred images on page-load (customer requirement). Currently, a HTTP request is made for each image via use of the HTML "img" tag. With the reduced latency and overall efficiency of websockets compared to HTTP or Ajax, I was hoping to improve performance by sending images over websockets instead.

Unfortunately, reading images from the server's file-system with NodeJS and sending them over websockets with SocketIO has been significantly slower than the traditional HTTP requests served over Apache. Below is my server code:

var express = require('express'),
    app = express(),
    http = require('http'),
    fs = require("fs"),
    mime = require('mime'),
    server = http.createServer(app),
    io = require('socket.io').listen(server);

server.listen(151);

io.sockets.on('connection',function(socket){
    socket.emit('connected');

    socket.on('getImageData',function(file,callback){
        var path = 'c:/restricted_dir/'+file;

        fs.readFile(path,function(err,data){
            if (!err){
                var prefix = "data:" + mime.lookup(path) + ";base64,";
                var base64Image = prefix+data.toString('base64');
                socket.emit('imageData',data,callback);
            }
        });
    });
});

I have also tried buffering with "createReadStream", but I saw no significant speed improvements with this. I should also note that it is desirable to receive the image data as a Base64-encoded dataURI so I can simply throw that into the "src" attribute of the "img" tag. I understand Base64 means roughly a 30% increase in the data's size, but even when using binary image data, it still takes about 10 times longer than HTTP.

EDIT: I suppose the real question here is, "are websockets really the best way to serve static files?" After further thought and additional reading, I strongly suspect the issue here is related to parallel processing. Since NodeJS operates on a single thread, maybe it is not the best solution for serving all these static image files? Does anyone have any thoughts on this?

CauselessEffect
  • 415
  • 5
  • 11
  • what does your client side code look like? – gherkins Nov 20 '13 at 15:16
  • Nothing special on the client side. Standard Socket.IO connection code then a loop around an array of filenames calling the following code: socket.emit('getImageData',file,callback); The callback simply throws the response data into image tags and appends them on the page. – CauselessEffect Nov 27 '13 at 14:08

1 Answers1

2

Browsers usually open multiple connections to the same server to perform requests in parallel, and can also perform multiple requests per single connection, whereas you only have one websocket connection.

Also, the combo fs.readFile()/Base64-encode/socket.emit() introduces a significant overhead, where a regular httpd can use system calls like sendfile() and don't even have to touch the file contents before they are being sent to the client.

The single-threaded nature of Node isn't an issue here, because Node can do I/O (which is what you're doing, minus the Base64-encoding) really well.

So I would say that websockets aren't very suitable for static file serving :)

robertklep
  • 198,204
  • 35
  • 394
  • 381
  • I was under the impression the browser could only have 2 HTTP connections open at one time for a specific page, glad to see that's changed. It definitely makes sense that NodeJS introduces overhead from the actual file-parsing. If I don't just walk away defeated and stick with HTTP, I think might check out the SPDY protocol. Thanks for your answer! – CauselessEffect Nov 27 '13 at 14:16
  • 1
    @CauselessEffect the number of connections a browser opens to the same host differs between browsers (see [here](http://stackoverflow.com/a/985704/893780) for an overview; the HTTP RFC states that at most 2 persistent connections per host should be opened); but still, it can perform multiple requests per single connection. – robertklep Nov 27 '13 at 14:24