【问题标题】:How do I talk to a client from within a loop using EventMachine?如何使用 EventMachine 从循环中与客户交谈?
【发布时间】:2024-01-18 15:28:01
【问题描述】:

我想在网站上实时显示我的程序进度(此处以循环为例)。

如果我有一个适当的 Ruby 服务器正在运行,客户端应该能够使用浏览器通过 websockets 连接到服务器,并且一旦连接,就可以实时接收有关新产生的进程的“更新”。无需轮询或 Ajax。

我对此进行测试的想法是使用客户端 JavaScript 在 EventMachine::WebSocket 和客户端浏览器之间建立双工通信。 构建一个循环,并将更新发送到浏览器客户端。

ruby_socket_server.rb:

require 'eventmachine'
require 'em-websocket'
@sockets = []
EventMachine.run do
    EventMachine::WebSocket.start(:host =>'localhost',:port =>8887) do |socket|
    socket.onopen do
        @sockets << socket
        (0..10).each do |i|
            puts "i am on loop #{i}" #to terminal window
            @sockets.each do |s|
                s.send "Just received notification of loop #{i}" 
                sleep(1) #pause for a second
            end
        end
    end
end

main.js:

socket = new WebSocket('ws://localhost:8887');
socket.onopen = function(){
    console.log('OPEN');
}
socket.onclose = function(){
    console.log('CLOSED');
}
socket.onmessage = function(msg){
    console.log(msg);
}

我希望它看起来像:

  • 服务器:我在循环 1。
  • 客户端:刚刚收到循环 1 的通知。
  • 服务器:我在循环 2。
  • 客户端:刚刚收到循环 2 的通知。

实际发生了什么:

  • 服务器:我在循环 1。
  • 服务器:我在循环 2。
  • 客户端:刚刚收到循环 1 的通知。
  • 客户端:刚刚收到循环 2 的通知。

我无法从 Ruby 循环中向客户端发送数据,即它最终会发送所有数据包,但不是在每次迭代期间都按预期发送。相反,它一直等到服务器循环完成,然后背靠背发送多个调用。

我在这里做错了什么?我正在寻找的东西是否可能?关于如何破解这个坚果的任何想法都会有所帮助。

【问题讨论】:

    标签: ruby websocket eventmachine em-websocket


    【解决方案1】:

    EventMachine 不是多线程的。所以如果你不让它运行,即如果你留在它的任何回调中,那么什么都不会发生。

    在您的示例中,您在回调中调用 sleep。这将阻止回调退出,因此 EM 无法运行,如果它无法运行,则无法发送您的消息。

    因此,必须完全围绕事件回调设置来构建任何 EM 服务器。

    这就是为什么它被称为事件机器。它需要稍微不同的思维方式,但一旦你掌握了窍门,你应该会没事的。

    这是一个关于如何使它工作的示例:

    EM.run do
      EM::WebSocket.start(:host =>'localhost',:port =>8887) do |ws|
        ws.onopen { |handshake|
          puts "New connection from #{handshake.origin}"
          loop = 1
          timer = EM.add_periodic_timer(1) {
            ws.send "Timer loop #{loop}"
    
            if (loop += 1) == 5
              EM.cancel_timer(timer)
              ws.send "Bye"
              ws.close_websocket
            end
          }
        }
      end
    end
    

    【讨论】:

    • 谢谢!!像魅力一样工作。
    • @Pandem1c - 酷。记住也要接受答案,这样人们就知道什么是有效的(单击左侧的复选标记)。谢谢。
    【解决方案2】:

    Rails5.0 可以使用ActionCable,它与WebSockets 有很好的兼容性。

    【讨论】: