Введение в Websockets в связке с Ruby (Sinatra)

В этой статье мы рассмотрим введение в WebSockets и реализацию базового приложения для чата с использованием EventMachine-WebSocket.

WebSockets — одна из фич в спецификации HTML5, которая позволяет клиенту и серверу взаимодействовать без использования AJAX или long-polling.

Что такое вебсокеты?

Согласно спецификации, вебсокеты это:

API, который позволяет веб-страницам использовать протокол WebSocket (определенный IETF) для двусторонней связи с удаленным хостом.

То есть, websockets могут использоваться вместо существующих решений для HTTP поллинга. Вебсокеты позволяют установить одно длительное TCP-соединение между клиентом и сервером. Это позволяет осуществлять полно дуплексный (двунаправленный) обмен сообщениями между обеими сторонами с очень небольшой задержкой.

Спецификация websockets определяет две новые схемы URI, ws: и wss:, для не зашифрованных и зашифрованных соединений соответственно.

Итак, начнем

Чтобы сделать чат-приложение, для начала создадим очень простое приложение Sinatra, работающее в EventMachine. Также используем сервер thin, так как он поддерживает EventMachine:

# Gemfile source 'https://rubygems.org' gem 'thin' gem 'sinatra' gem 'em-websocket'
# app.rb require 'thin' require 'sinatra/base' require 'em-websocket' EventMachine.run do class App < Sinatra::Base get '/' do erb :index end end # our WebSockets server logic will go here App.run! :port => 3000 end
# views/index.erb <!doctype html> <html> <head> </head> <body> # WebSockets Chat App <div id='chat-log'> <input type='text' id='message'> <button id='disconnect'>Disconnect</button> <script src='//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js'></script> <script> // where our WebSockets logic will go later </script> </body> </html>

Запускаем:

bundle exec ruby app.rb

Начало положено! Далее, разберемся со всем со стороны сервера.

WebSockets сервер

Для начала, настроим сервер для работы с EventMachine и обработки websocket соединения. Это делается довольно просто с использованием гема em-websocket, нам даже не нужно беспокоиться о хедерах для websocket handshake:

EventMachine.run do # ... [previous Sinatra stuff] @clients = [] EM::WebSocket.start(:host => '0.0.0.0', :port => '3001') do |ws| ws.onopen do |handshake| @clients &lt;&lt; ws ws.send 'Connected to #{handshake.path}.' end ws.onclose do ws.send 'Closed.' @clients.delete ws end ws.onmessage do |msg| puts 'Received Message: #{msg}' @clients.each do |socket| socket.send msg end end end # ... [run Sinatra server] end

Этот код позаботится о handshake вебсокета с клиентами, а также о бэкенде чата. Далее настроим клиентскую часть:

WebSockets клиент

Теперь настроим клиентскую часть, чтобы она могла взаимодествовать с сервером вебсокетов, который мы только что настроили. Сервер будет принимать любые сообщения, отправляемые клиентом и бродкастить их на все остальные клиенты.

Для начала добавим базовую функцию для отображения сообщений на странице:

function addMessage(msg) { $('#chat-log').append('' + msg + ''); }

Теперь настроим соединение с нашим вебсокет сервером так, чтобы клиент подключался сразу как только браузер будет готов:

var socket, host; host = 'ws://localhost:3001'; function connect() { try { socket = new WebSocket(host); addMessage('Socket State: ' + socket.readyState); socket.onopen = function() { addMessage('Socket Status: ' + socket.readyState + ' (open)'); } socket.onclose = function() { addMessage('Socket Status: ' + socket.readyState + ' (closed)'); } socket.onmessage = function(msg) { addMessage('Received: ' + msg.data); } } catch(exception) { addMessage('Error: ' + exception); } } $(function() { connect(); });

Дальше можем добавить логику отправки сообщения по нажатию кнопки Enter. Этот код отправит сообщение на сервер и даст пользователю знать, что сообщение было отправлено:

function send() { var text = $('#message').val(); if (text == '') { addMessage('Please Enter a Message'); return; } try { socket.send(text); addMessage('Sent: ' + text) } catch(exception) { addMessage('Failed To Send') } $('#message').val(''); } $('#message').keypress(function(event) { if (event.keyCode == '13') { send(); } });

В конце, добавим возможность пользователям отключаться от сервера, если необходимо:

$('#disconnect').click(function() { socket.close() });

Это завршает работу над клиентской частью. Теперь клиент откроет вебсокет соединение с нашим сервером, будет отправлять/получать сообщения, и отключаться если этого захочет пользователь.

Заключение

Иии..Теперь у нас есть рабочий чат сервис! Чтобы его запустить, просто запускаем сервер:

bundle exec ruby app.rb

Теперь можно зайти на http://localhost:3000/, и увидеть чат в действии. Можно открыть в нескольких браузерах чтобы потестировать.

11
4 комментария

Статья скорее для Хабра)

2

На хабре стыдно с очередной статьёй про Hello World на web-сокетах показываться, а здесь чуток обнажил область баш скрипта и все думают, что ты х4к3рь.

upd: тем более когда фигурирует jquery

Чувак, смотрю ты в 2012-м, закупайся биткоином!

1