はじめに
RubyでNEMのWebSocketを扱った前例が無かったので調査しました。WebSocketやSTOMPの仕組みなどの解説はここでは省略します。
実装
require "json"
require 'websocket-client-simple'
require 'stomp_parser'
require 'securerandom'
host = "http://alice3.nem.ninja"
port = 7778
path = "/w/messages/"
server = format("%0#{3}d", SecureRandom.random_number(10**3))
strings = [('a'..'z'), ('0'..'5')].map { |i| i.to_a }.flatten
session = (0...8).map { strings[rand(strings.length)] }.join
endpoint = host + ":" + port.to_s + path + server.to_s + "/" +session + "/websocket/"
ws = WebSocket::Client::Simple.connect(endpoint)
ws.on :open do
connect_header = {"accept-version":"1.1,1.0","heart-beat":"10000,10000"}
stomp_connect = '["' + StompParser::Frame.new("CONNECT", connect_header,"").to_str + '"]'
ws.send(stomp_connect)
end
ws.on :message do |msg|
if msg.data[0] == "o"
end
if msg.data[0] == "h"
end
if msg.data[0] == "a"
data = msg.data
data.slice!(0,data.index("[")+2)
data.slice!(data.rindex("]")-1,data.length)
data.gsub!("\\n","\n")
data.gsub!("\\r","\r")
data.gsub!("\\/","\/")
data.gsub!("\\u0000","\u0000")
data.gsub!("\\\"","\"")
parser = StompParser::Parser.new
parser.parse(data) do |frame|
puts "We received #{frame.command} frame with #{frame.body} and headers #{frame.headers}!"
if frame.command == "CONNECTED"
blocks_header = {"id":"sub-0","destination":"/blocks"}
stomp_subscribe_blocks = '["' + StompParser::Frame.new("SUBSCRIBE", blocks_header, "").to_str + '"]'
ws.send(stomp_subscribe_blocks)
end
if frame.command == "MESSAGE"
result = JSON.parse(frame.body)
p result["timeStamp"]
end
end
end
end
ws.on :error do |e|
p(e)
end
loop do
end
解説
以下のような動作を行っています。
-
接続先のURLを生成する
- 接続先はhttp://ホスト名:websocket用のポート/w/messages/3桁の数字で構成された乱数/8桁の小文字の英字と0~5の数字で構成された乱数/となります。
-
WebSocketClientを用いて接続する
- websocket-client-simpleを使用させていただきました。
-
open時にCONNECTコマンドを送信する
- 以下のSUBSCRIBEコマンドの場合もですが、STOMPパーサーを使うとハッシュから楽にSTOMPに対応した文字列を生成できます。
- SockJSの仕様か、生成したフレームは[""]で覆わないとサーバー側からの返信が来ません。
-
サーバーから通知が来たらデータの最初の一文字目で分岐する
- "o"はサーバーがframeを開いた時に送信されるようです。
- "h"はサーバーからのハートビートです。
- "a"は[]内にサーバーからのデータが含まれています。
-
CONNECTEDコマンドを含む通知が来たらSUBSCRIBEコマンドを送信する
- 受信時のデータは余分なバックスラッシュ等余計なものが多く、そのままではパースできないのでここで綺麗にしています。
- このプログラムでは最新ブロック高監視を行っています。
-
MESSAGEコマンドを含む通知が来たらbodyをパース後データを表示
- MESSAGEコマンドを含むフレームのbodyは通常のAPIと同じくjsonで記述されているので、簡単にパースできます。
詳細
こちらにWebSocketなどの技術解説や調査方法についてまとめました。
この調査を元にしてrubyでnemのwebsocketを扱えるgemを作りました。詳しくは こちらへどうぞ。
参考
github - varvet/stomp_parser
github - shokai/websocket-client-simple
github - sockjs/sockjs-client
github - sockjs/sockjs-protocol
田舎からGeekを目指す - NEMのWebSocket通信を使ってみた