RubyでつくるWebSocketサーバーにprotocol-websocket gemを取り入れる その4 - @ledsun blog で、HTTPリクエスト文字列、HTTPレスポンス文字列の処理をWEBrickで行うことにしました。 今回はWebSocketのFrameの読み書きをprotocol-websocketを使って行います。
require 'socket' require 'webrick' require 'protocol/websocket/headers' require 'protocol/websocket/framer' require 'protocol/websocket/text_frame' def read_headers_from(socket) request = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) request.parse(socket) request.header end def calculate_accept_nonce_from(headers) key = headers[Protocol::WebSocket::Headers::SEC_WEBSOCKET_KEY].first Protocol::WebSocket::Headers::Nounce.accept_digest(key) end server = TCPServer.new 'localhost', 2345 loop do # ここら辺はecho_server.rbと同じ。純粋なTCP通信 socket = server.accept # HTTPリクエストheaderを読み込む headers = read_headers_from socket # WebSocketリクエストかどうかを判定する unless headers["upgrade"] = Protocol::WebSocket::Headers::PROTOCOL puts "Not a websocket request" socket.close next end response_key = calculate_accept_nonce_from headers puts "response_key: #{response_key}" response = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP) response.status = 101 response.upgrade! Protocol::WebSocket::Headers::PROTOCOL response['Sec-WebSocket-Accept'] = response_key # ソケットそのものはHTTP通信で使われているものと同じ response.send_response socket puts 'Handshake response sent' # ここからはWebSocket通信 framer = Protocol::WebSocket::Framer.new(socket) request_frame = framer.read_frame raise 'frame is not a text' unless request_frame.is_a? Protocol::WebSocket::TextFrame puts "Received: #{request_frame.unpack}" # クライアントにデータを返す response_message = "Loud and clear!" response_frame = Protocol::WebSocket::TextFrame.new(true, response_message) response_frame.write(socket) socket.close end
バイナリ読んでOPCODEを特定して、マスクを剥がしてPackする一連の面倒な処理が、シュッと書けました。 protocol-websocketすごい!便利。 このあとは、リファクタリングして、Async::WebSocketを使ったクライアントと通信できるかなど、試したいとおもいます。
Protocol::WebSocket::FramerとProtocol::WebSocket::Framerの設計がかっこいい。
入出力の型をStreamにしてあるおかげで、普通のSocketも読み書きできるし、async-websocketで使っているAsync::IO::Streamも使える。