How to send a keep-alive packet through websocket in ruby on rails
You can use Websocket Eventmachine Client gem to send hearbeat:
require 'websocket-eventmachine-client'
EM.run do
ws = WebSocket::EventMachine::Client.connect(:uri => 'wss://bitcoin.toshi.io/')
puts ws.comm_inactivity_timeout
ws.onopen do
puts "Connected"
end
ws.onmessage do |msg, type|
puts "Received message: #{msg}"
end
ws.onclose do |code, reason|
puts "Disconnected with status code: #{code}"
end
EventMachine.add_periodic_timer(15) do
ws.send "{}"
end
end
You can setup timer for EventMachine with EM::add_periodic_timer(interval_in_seconds)
, and then send your heartbeat with it. Keeping the WebSocket connection alive
The connection is closed each time after handle
. You should rather stay there reading incoming data:
# incoming connection
def setup(self):
print "connection established", self.client_address
def handle(self):
while 1:
try:
self.data = self.request.recv(1024).strip()
# incoming message
self.headers = self.headsToDict(self.data.split("\r\n"))
# its a handshake
if "Upgrade" in self.headers and self.headers["Upgrade"] == "websocket":
key = self.headers["Sec-WebSocket-Key"]
accept = b64encode(sha1(key + MAGIC).hexdigest().decode('hex'))
response = "HTTP/1.1 101 Switching Protocols\r\n"
response += "Upgrade: websocket\r\n"
response += "Connection: Upgrade\r\n"
response += "Sec-WebSocket-Accept: "+accept+"\r\n\r\n"
print response
self.request.send(response)
# its a normal message, echo it back
else:
print self.data
self.request.send(self.data)
except:
print "except"
break
how can I inject a keep-alive into a websocket handler in akka-http?
The only way to control how connections are dropped server-side is to change the idle-timeout
(see the docs here and here).
You can set
idle-timeout = infinite
to completely disable the timeout.If you want to use keep alive messages, they need to be sent from the client side. The .keepAlive
combinator is the easiest way to get them if your client is based on Akka Streams.
Passing progress messages from server to frontend in Rails 4
Your question was relatively vague, so apologies if my answer is equally:
WebSockets Or SSE (Server Sent Events)
We just implemented something similar to this with an app which deletes multiple records, and sends updates on which have been deleted
You're right with pub/sub, and I'll attempt to explain the underlying tech. It's actually a similar process for SSE or WebSockets
Receiving "Live" Data
The process of receiving "live" data from the server is to use the ActionController::Live controller inside Rails. This works with the response.headers['Content-Type'] = 'text/event-stream'
HTTP mime-type to send small packets of data to a Javascript "listener" (which you initialize on your front-end)
Whether you use SSE or WebSockets, you have to have a "live" controller action (which creates the live-information), and also have a Javascript EventListener in place to capture the real-time data in the browser
The way you do this changes with each technology, but it's actually a similar process for both SSE & WebSockets
SSE's
Server Sent Events is an HTML5 technology which basically allows you to receive (not send) updated data by opening an asynchronous connection to an endpoint (your Rails ActionController::Live action) on your server, which will publish the data you need
You'd typically set up this using Redis (to compile the data into JSON) and send it using Rails' ActionController::Live functionality. Your Javascript will "listen" for the event being triggered, thus allowing you to capture the real-time data sent from the server & manipulate it on the front end
The problem with SSE's is that it uses long-polling to continually "ping" the endpoint in an attempt to get the latest information. Although Puma allows for multi-threading, we were unable to get it to run the connection concurrently, degrading performance significantly (basically, sending a request every second eats up your connections)
WebSockets
Websockets are a similar technology to SSE's, but with a major difference -- they allow a perpetual connection:
This means that you can send and receive data, as well as only connecting once & maintaining the connectivity, regardless of what you do interim. We found this to be a far more stable solution than SSE's, as it removes the requirement for long-polling, and, if you can keep authentication correct, gives you the ability to receive data correctlyWebSocket is a protocol providing full-duplex communications channels
over a single TCP connection
All the big-hitters use WebSockets (including StackOverflow), and it's actually relatively simple to set up:
"Real Time" Setup
You need:
- Rails ActionController::Live Function & Endpoint (route)
- Third-Party Websocket Integration (Pusher)
- Javascript EventListeners (to handle returned data)
#app/controllers/resources_controller.rb (inherited resources)
def destroy
element = @resource.find(params[:id])
element.destroy
Pusher['private-user-' + current_user.id.to_s].trigger('my_event', {
message: "Removed #{element.title}"
})
end
#app/assets/javascripts/users.js.coffee.erb
pusher = new Pusher("******************",
cluster: 'eu'
)
channel = pusher.subscribe("private-user-#{gon.user_id}")
channel.bind "my_event", (data) ->
alert data.message
This opens a persisting connection to the pusher app, which then handles our data. Whenever pusher receives our data, it sends the update to us, allowing us to alert the user to the message
I know it's a simplistic overview, but I hope it helps
How to use ActionCable as API
While mwalsher's solution was extremely helpful to me, I recently found a pull request on the Rails repository with an official solution to my question.
https://github.com/rails/rails/pull/24991
I assume, in the near future this will be added to the main documentation. Here is the link to the official actioncable npm package: https://www.npmjs.com/package/actioncable
You can use it similar to mwalsher's solution with any JS app. Just install the npm package:
npm install actioncable --save
Here the JS example from the documentation:ActionCable = require('actioncable')
var cable = ActionCable.createConsumer('wss://RAILS-API-PATH.com/cable')
cable.subscriptions.create('AppearanceChannel', {
// normal channel code goes here...
});
Edit: The pull request has been merged for a while, now and the description is part of the official Readme - just not yet in the Rails Guides. Websocket client (browser) receives a zero-length message - what is it?
I think this is just a Safari annoyance. The current released versions of Safari implement the older WebSockets protocol version and the implementation works but it is a bit lacking. The 0 length messages issue is pretty tame. A more serious issue is the inability to properly close a connection (basically doesn't close the actual socket until the page is reloaded).
Try the same test with a recent Chrome. If you don't see the problem there then it's just a Safari issue. If you still see 0 length messages, then it is likely that the PHP websocket server is sending 0 length messages (perhaps as a sort of poor man keep-alive).
Related Topics
Define_Method with Predefined Keyword Arguments
Crontab Not Running Ruby Script
How to Override a Variable in a Ruby Subclass Without Affecting The Superclass
Ruby How to Generate a Tree Structure Form Array
Why Is Split(' ') Trying to Be (Too) Smart
Does The Rails Orm Limit The Ability to Perform Aggregations
Why Does Array.To_S Return Brackets
How to Have Two Columns in One Table Point to The Same Column in Another with Activerecord
How to Create Line Breaks in Ruby
How Does String.Unpack Work in Ruby
Gitlab Configuration Issues:: Nginx Unicorn Port Conflict
Ruby Fails on Osx Lion with Rbenv
Why Can't The Mail Block See My Variable
Many to Many Table with an Extra Column in Rails
Why Do Ruby People Say They Don't Need Interfaces
Getting a "Connection Reset by Peer" Error When Hitting Google Contacts API
Ruby Symbols Are Not Garbage Collected!? Then, Isn't It Better to Use a String