Skip to content

Latest commit

 

History

History
361 lines (227 loc) · 18.2 KB

README.md

File metadata and controls

361 lines (227 loc) · 18.2 KB

SocketStream!

Latest release: 0.2.7 (view changelog)

Twitter: @socketstream
Google Group: http://groups.google.com/group/socketstream
IRC channel: #socketstream on freenode

Read Full 0.2 Announcement

Introduction

SocketStream is a new Node.js web framework dedicated to creating single-page real time websites.

Unlike traditional web frameworks, there's no routing, AJAX or partials to think about. Instead all application data is streamed over websockets as high-speed bi-directional RPC calls; allowing you to create entirely new ultra-responsive applications that will amaze your users.

SocketStream 0.2 is feature-rich and relatively stable, thanks to a growing community of users. You should find most of the functionality you need to build a good-sized real time web app available today, along with the ability to distribute incoming requests over multiple boxes should your app need to scale.

Update as of 9th December 2011

Work on SocketStream 0.3 is coming along well and we're very excited about the power and flexibility of the new modular design. We're aiming to release the first alpha version by the end of December. Read about the new features and changes coming in SocketStream 0.3 here: https://groups.google.com/forum/#!topic/socketstream/AFwFAPMKzjU

Follow @socketstream for the latest developments and thinking. Website coming soon.

Features

General

  • True bi-directional communication using websockets (or Socket.IO 0.8 fallbacks). No more slow, messy AJAX!
  • Write all code in CoffeeScript or JavaScript - your choice
  • Share code between the client and server. Ideal for business logic and model validation
  • Can use Redis for fast session retrieval, pub/sub, list of users online, and any other data your app needs instantly
  • Effortless, scalable, pub/sub baked right in - including Private Channels
  • In-built User model - with modular internal authentication (authentication with external services e.g. Facebook Connect coming soon)
  • Interactive Console - just type socketstream console and invoke any server-side or shared method from there
  • API Trees - offer a simple, consistent way to namespace and organize large code bases
  • Uses Connect - hook in 3rd-party middleware or write your own. Custom code executes first for maximum flexibility and speed
  • Server-side Events - run custom code server-side when clients initialize or disconnect
  • MIT License

Client Side

  • Works great with Chrome, Safari and now Firefox 6 using native websockets
  • Compatible with older versions of Firefox and IE thanks to configurable fallback transports provided by Socket.IO
  • Works well on iPads and iPhones using Mobile Safari (iOS 4.2 and above), even over 3G
  • Integrated asset manager - automatically packages and minifies all client-side assets
  • Bundled with jQuery - though not dependent on it. Will work great with Zepto and other libraries
  • Bundled with jQuery templates for ease - works like partials in Rails
  • Easily add additional client libraries such as Underscore.js
  • Initial HTML sent to the browser can be written in Jade or plain HTML
  • Supports Stylus or Less for CSS (works great with plain CSS too)

Distributed Hosting

  • Out-of-the-box HTTPS support with automatic HTTP redirects
  • Distributed frontend and backend architecture separated by a light-weight RPC abstraction layer
  • Allows SocketStream to run lightning-fast in a single process with no C libraries to install (ideal for Cloud9 IDE)
  • When you need to scale up, easily spread the load over multiple CPU cores or additional boxes using ZeroMQ
  • Near linear scalability when spreading CPU-intensive tasks over multiple backend servers (run socketstream benchmark to experiment)
  • Front end servers can be completely isolated from Redis and your databases. They just need to talk to the box running socketstream router
  • Internal RPC layer designed to easily support additional transports and serialization formats in the future

Optional Modules (will only load if enabled)

  • HTTP/HTTPS API - all server-side code is automatically accessible over a high-speed request-based API
  • Users Online - automatically keeps track of users online
  • Plug Sockets - high-speed connections to any external server or legacy apps using ZeroMQ. Over 20 languages supported
  • Rate Limiting - basic rate limiting to prevent scripted DDOS attacks

How does it work?

SocketStream automatically compresses and minifies all the static HTML, CSS and client-side code your app will ever need and sends this through the first time a user visits your site.

From then on all application data is sent and received as serialized JSON objects over a websocket tunnel (or Socket.IO fallback), instantly established when the client connects and automatically re-established if broken.

All this means no more connection latency, HTTP header overhead, or clunky AJAX calls. Just true bi-directional, asynchronous, 'streaming' communication between client and server.

What can I create with it?

SocketStream is a perfect fit for all manner of modern applications which require real-time data (chat, stock trading, location monitoring, analytics, etc). It's also a great platform for building real-time HTML5 games. However, right now it would make a poor choice for a blog or other content-rich site which requires unique URLs for search engine optimization.

Tutorials

Building Real-time CoffeeScript Web Applications With SocketStream by Addy Osmani

Example Apps

These apps are all in their infancy at the moment, but looking at the code is a great way to start learning SocketStream:

SocketChat - simple group chat

Dashboard - real-time dashboard with configurable widgets (uses MongoDB)

SocketRacer - multi-player racing game

Instastream - real time Instagram viewer (needs to be hosted externally)

Example 1: Basic RPC

The key to using SocketStream is the SS global variable which can be called anywhere within your server or client-side code.

For example, let's write a simple server-side function which squares a number. Make a file called /app/server/app.js and put this in it:

exports.actions = {

  square: function (number, cb) {
    cb(number * number);
  }
}

To call this from the browser make a file called /app/client/app.js and put this in it:

exports.square = function (number) {

  SS.server.app.square(number, function (response) {
    console.log(number + ' squared is ' + response);
  });
}

Restart the server, refresh your page, then type this into the browser console:

SS.client.app.square(25)

And you will see the following output:

25 squared is 625

The eagle-eyed among you will notice SS.client.app.square(25) actually returned undefined. That's fine. We're only interested in the asynchronous response sent from the server once it has processed your request.

You can also call this server-side method using the optional HTTP API (enabled by default) with the following URL:

/api/app/square?25                        # Hint: use .json to output to a file

Or even directly from the server-side console (type socketstream console) OR the browser's console OR another server-side file:

SS.server.app.square(25, function(x){ console.log(x) })

Note: The console.log callback is automatically inserted if you're calling SS.server methods from the browser.

You will notice by now that the SS variable is similar to the dollar sign $ in jQuery - it's the main way into the SocketStream API. We do our best to keep the API between client and server identical wherever possible.

Ready for something a bit more advanced? Let's take a look at reverse geocoding using HTML5 geolocation...

Example 2: Reverse Geocoding

From now on all examples are in CoffeeScript, the default for SocketStream (though you're completely free to use pure JavaScript throughout your project if you prefer).

For the server code, create the file /app/server/geocode.coffee and paste in the following code:

exports.actions =

  lookup: (coords_from_browser, cb) ->
    host = 'maps.googleapis.com'
    r = coords_from_browser.coords
    http = require('http')
    google = http.createClient(80, host)
    google.on 'error', (e) -> console.error "Unable to connect to #{host}"
    request = google.request 'GET', "/maps/api/geocode/json?sensor=true&latlng=#{r.latitude},#{r.longitude}"
    request.end()
    request.on 'error', (e) -> console.error "Unable to parse response from #{host}"
    request.on 'response', (response) => parseResponse(response, cb)

parseResponse = (response, cb) ->  # note: private methods are written outside of exports.actions
  output = ''
  response.setEncoding('utf8')
  response.on 'data', (chunk) -> output += chunk
  response.on 'end', ->
    j = JSON.parse(output)
    result = j.results[0]
    cb(result)

To capture your location and output your address, lets's add this code in /app/client/app.coffee

# Note: the SS.client.app.init() method automatically gets called once the socket is established and the session is ready
exports.init = ->
  SS.client.geocode.determineLocation()

Then, purely to demonstrate client-side namespacing (see section below), let's create a new file called /app/client/geocode.coffee and paste this in:

exports.determineLocation = ->
  if navigator.geolocation
    navigator.geolocation.getCurrentPosition(success, error)
  else
    alert 'Oh dear. Geolocation is not supported by your browser. Time for an upgrade.'

# Private functions

success = (coords_from_browser) ->
  SS.server.geocode.lookup coords_from_browser, (response) ->
    console.log response
    alert 'You are currently at: ' + response.formatted_address

error = (err) ->
  console.error err
  alert 'Oops. The browser cannot determine your location. Are you online?'

Run this code and you should see your current location pop up (pretty accurate if you're on WiFi). Of course, you'll need to handle the many and various errors that could go wrong during this process with a callback to the client.

Bonus tip: Want to run this again? Just type SS.client.geocode.determineLocation() in the browser console. All 'exported' client-side functions can be called this way.

Example 3: Pub/Sub

Want to build a chat app or push an notification to a particular user?

First let's listen out for an event called 'newMessage' on the client:

exports.init = ->
  SS.events.on('newMessage', (message) -> alert(message))

Then, assuming we know the person's user id, we can publish the event directly to them. On the server side you'd write:

exports.actions =

  testMessage: (user_id) ->
    SS.publish.user(user_id, 'newMessage', 'Wow this is cool!')

Pretty cool eh? But it gets better. We don't have to worry which server instance the user is connected to. The message will always be routed to the correct server as each SocketStream server subscribes to the same instance of Redis.

Want to know how to broadcast a message to all users, or implement private channels? Take a look at the 'More Pub/Sub' section in the documentation below.

Requirements

Node 0.6.X

NPM 1.0 (Node Package Manager) or above

Getting Started

Ready to give it a whirl? SocketStream is published as an npm package easily installed with:

sudo npm install socketstream -g

To generate a new empty SocketStream project type:

socketstream new <name_of_your_project>

To start your app cd into the directory you've just created and type:

socketstream start

If all goes well you'll see the SocketStream banner coming up, then you're ready to visit your new app at:

http://localhost:3000

Documentation Index

All documentation is in /doc/guide/en

Developing your app

Optional modules

These modules will only load if enabled

Deploying

Other


Tests

We have begun to write tests for parts of SocketStream which are unlikely to change in the near future. Currently these live in a separate project any only cover a small number of features - but it's a start. We've chosen Jasmine so far but still need to decide how to organize the files and where to run the specs (as websocket tests are far easier to run in the browser).

Known Issues

  • New files added to /lib/client files will not be detected until you restart the server and touch one of the /lib/client files. We will fix this
  • Any manipulation of $('body') using jQuery, e.g. $('body').hide(), under Firefox 4 disrupts the flashsocket connection. Until we figure out what's causing this bizarre bug, best avoid calling $('body') in your code.

Core Team

  • Owen Barnes (socketstream & owenb)
  • Paul Jensen (paulbjensen)
  • Alan Milford (alz)
  • Addy Osmani (addyosmani)

Credits

Thanks to Guillermo Rauch (Socket.IO), TJ Holowaychuk (Stylus, Jade), Jeremy Ashkenas (CoffeeScript), Mihai Bazon (UglifyJS), Isaac Schlueter (NPM), Salvatore Sanfilippo (Redis), Justin Tulloss (Node ZeroMQ bindings) and the many others who's amazing work has made SocketStream possible. Special thanks to Ryan Dahl (creator of node.js) for the inspiration to do things differently.

Thanks!

SocketStream is kindly sponsored by AOL.

License

SocketStream is released under the MIT license.