Skip to content

Instantly share code, notes, and snippets.

@Shinpeim
Created June 8, 2012 10:56
Show Gist options
  • Save Shinpeim/2894978 to your computer and use it in GitHub Desktop.
Save Shinpeim/2894978 to your computer and use it in GitHub Desktop.
メッセージングサーバの習作
# -*- coding: utf-8 -*-
require 'eventmachine'
require 'json'
require 'hashie'
#ログイン済みのコネクションハンドラーのコンテナ
class LoginedConnectionContainer
@@users = {}
def self.add(user_id,connection)
@@users[user_id] = connection
end
def self.fetch(user_id)
@@users[user_id]
end
def self.remove(user_id)
@@users.delete(user_id)
end
def self.list
@@users.keys
end
end
class ConnectionHandler < EM::Connection
def post_init
@state = ConnectionState::Connected.new(self)
@buffer = ''
end
#クライアントからデータ送られて来たときに呼ばれる
#Stateに委譲
def receive_data(chunk)
@buffer += chunk
return unless @buffer =~ /\r\n$/
line = @buffer.gsub(/[[:cntrl:]]/,"").encode("UTF-8") rescue raise("invalid utf-8 string")
@buffer = ''
data = Hashie::Mash.new(JSON.parse(line)) rescue raise("invalid JSON")
next_state = @state.process(data)
@state = next_state
rescue => e
send_error(e.message)
end
#接続切れたときに呼ばれる
#Stateに委譲
def unbind
@state.unbind
end
def send_line(data)
send_data(data + "\r\n")
end
private
def to_valid_utf8(line)
return
end
def send_error(msg)
error = JSON.generate({"event" => "error","message" => msg})
send_line(error)
close_connection_after_writing
end
end
#State
module ConnectionState
#接続後すぐの状態
class Connected
def initialize(connection)
@connection = connection
end
def process(data)
raise "user_id is required in login request" unless data.user_id
LoginedConnectionContainer.add(data.user_id, @connection)
return Logined.new(data.user_id)
end
def unbind
# do nothing
end
end
#ログイン後の状態
class Logined
def initialize(user_id)
@user_id = user_id
end
def process(data)
raise "'to' is required in message" unless data.to
raise "'message' is required in message" unless data.message
msg = JSON.generate({"event" => "message", "from" => @user_id, "message" => data.message})
to = LoginedConnectionContainer.fetch(data.to)
return unless to
to.send_line(msg)
return self
end
def unbind
LoginedConnectionContainer.remove(@user_id)
end
end
end
EM::run do
host,port = "0.0.0.0", 6789
EM.add_periodic_timer(1) do
p LoginedConnectionContainer.list
end
EM::start_server host, port, ConnectionHandler
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment