Suranyami

rorosyd

January 11 2012, 3:02 PM  by David Parry

EventMachine + WebSockets

This is the notes from a talk I gave at #rorosyd on the 10th of January 2012.

The demo code is available at: [https://github.com/suranyami/socket_demo][]

So, what’s EventMachine?

  • Reactor pattern
  • Extremely high performance
  • Doesn’t need threads, concurrency
  • Addresses the C10K problem
  • Callbacks
  • Fast, low memory overhead

EventMachine supports lots of protocols!

There is a great list of currently implemented protocols at https://github.com/eventmachine/eventmachine/wiki/Protocol-Implementations which includes:

  • HeaderAndContent
  • SMTPServer
  • Stomp
  • Socks4
  • ObjectProtocol
  • SASLauth
  • LineAndText
  • LineText2
  • HTTPClient
  • HTTPClient2
  • AMQP
  • MySQL
  • SMTP
  • Postgres
  • MemCache
  • XMPP
  • DNS
  • PowerDNS
  • ICMP
  • XML Push Parser
  • Redis
  • MongoDB
  • CouchDB
  • Beanstalk
  • SNMP
  • HTTPRequest
  • HTTPServer
  • PubSubHubbub
  • Proxy
  • WebSocket
  • SMPP
  • RPC
  • IRC
  • Spec
  • Cassandra
  • Thrift
  • Solr
  • Syslog
  • Amazon S3
  • OSCAR (AIM, ICQ)
  • RServe
  • SSH

How to learn about EventMachine?

PeepCode

EventMachine Introduction

Example of a Trivial Telnet Server

require 'eventmachine'
class Echo < EM::Connection
  def receive_data(data)
    send_data(data)
  end
end

EM.run do
  EM.start_server(“0.0.0.0”, 10000, Echo)
end

So, what’s a WebSocket?

  • Used for “push”, real-time bidirectional updates
  • HTML 5, proposed RFC, too.
  • In all modern browsers Chrome Safari, Safari Mobile Firefoxx but NOT Android browser (FFS! Why?) ** IE 10, but not IE 6-9 (who cares!)
  • iOS native library + elsewhere
  • Combining EventMachine + WebSocket = em-websocket

Where would I use it?

  • Chat
  • Multiplayer Games
  • Real-time dataviz
  • Real-time news feeds
  • Real-time anything

Trivial WebSocket server

require 'eventmachine'
require 'em-websocket'

conf = {:host => “0.0.0.0”, :port => 8080}

EM.run {
  EventMachine::WebSocket.start(conf) do |ws|
    ws.onopen {ws.send "Hello Client"}
    ws.onclose { puts "Connection closed" }
    ws.onmessage {|msg| ws.send "Echo: #{msg}"}
end }

A Less Trivial example

So, I wanted something to demo that wasn’t trying to do to much but demonstrated the real-time nature of WebSockets, so I built this little socket_demo site:

  • Sinatra serves HTML, JS (CoffeeScript), CSS
  • Running an EM-WebSocket server
  • Each guest decides whether a block is green or red (default’s white)
  • Joining/leaving adds/removes blocks
  • New guest gets snapshot of world

Useless, but non-trivial!

CoffeeScript

Assign an id to socket/guest

EM-Websocket Server:

class Demo constructor: –>
    if WebSocket?
      window.socket = new WebSocket("ws://192.168.1.3:7070")
    else
      window.socket = new MozWebSocket("ws://192.168.1.3:7070")

Register ourselves

CoffeeScript:

window.socket.onopen = –>
  window.socket.send JSON.stringify({kind: “register”})

EM-Websocket Server:

socket.onmessage do |msg|
  puts "Server Received #{msg}"
  id = @sockets[socket][“id”]
  incoming = ActiveSupport::JSON.decode(msg)
  case incoming[“kind”]
    when "register"
      socket.send(register_message(id).to_json)
      broadcast(id)
    else
      ## Send “add” event to everyone

EM-Websocket Server:

def send_to_all(message) @sockets.each do |destination|`
    destination.send(message.to_json)
end

CoffeeScript:

```coffeescript
window.socket.onmessage = (mess) –>
  data = jQuery.parseJSON(mess.data)
  switch data[“kind”]
  when "add"
      window.add_player data["id"], data["color"]

Send click events

$(“#red”).click (e) => window.socket.send JSON.stringify {kind: “update”, color: “red”}

$(“#green”).click (e) => window.socket.send JSON.stringify {kind: “update”, color: “green”}

Server gets a message

socket.onmessage do |msg|
  id = @sockets[socket][“id”] incoming = ActiveSupport::JSON.decode(msg)
  case incoming[“kind”]
    when “register”
      socket.send(register_message(id).to_json)
      broadcast(id)
    else
      if incoming["color"]
        color = incoming["color"]
        message = {"kind" => "update", "id" => id, "color" => color}
        @sockets[socket]["color"] = color
        send_to_all(message)
    end
  end
end

Live Demo

Then I gave a live demo where I used my laptop as an unsecured base station, and people connected to it with their iPhones and started clicking on the red/green buttons:

wifi: suranyami (no password) url: test.local:4567

How to do testing with EventMachine?

em-spec https://github.com/joshbuddy/em-spec

This demo code viewable here: [https://github.com/suranyami/socket_demo][]

September 11th, 2013 2:19pm