【问题标题】:Sinatra using a websocket client to respond to a http requestSinatra 使用 websocket 客户端响应 http 请求
【发布时间】:2018-02-09 18:48:47
【问题描述】:

我正在编写一个我想成为 RESTful 的 Sinatra Web 服务器,但问题是它必须与另一个仅通过 Web 套接字通信的服务器进行交互。所以,这需要发生:

  1. 一个请求从客户端进入我的 Sinatra 服务器
  2. 我的服务器打开到外部服务器的 Web 套接字
  3. 我的服务器异步等待来自外部服务器的消息和东西,直到套接字关闭(这应该只需要两百毫秒左右)
  4. 我的服务器向客户端发回响应

我确信这并不太复杂,但我有点坚持。基本上,如果整个 Web 套接字逻辑可以包装在一个函数中,那么该函数可以被设置为阻塞的,就是这样。但我不知道如何包装网络套接字逻辑并阻止它。你怎么看?下面是我所拥有的简化版本。

require 'sinatra'
require 'websocket-client-simple'

get '/' do
     ws = WebSocket::Client::Simple.connect(' ws://URL... ')

     ws.on :message do
          puts 'bar'
     end

     ws.on :close do
          # At this point we need to send an HTTP response back to the client. But how?
     end

     ws.on :open do
          ws.send 'foo'
     end

end

编辑

经过进一步思考,我意识到可以使用线程停止和线程唤醒来完成此操作。这感觉相当复杂,我不知道如何用 Ruby 正确地做到这一点,但这就是想法:

require 'sinatra'
require 'websocket-client-simple'

get '/' do
    socketResponse('wss:// ... URL ...')

    'Got a response from the web socket server!'
end

def socketResponse(url)
    thread = Thread.new do

        ws = WebSocket::Client::Simple.connect(url)

        ws.on :message do
            puts 'bar'
            # Maybe store each response in a thread-safe array to retrieve later or something
        end

        ws.on :close do
            thread.run
        end

        ws.on :open do
            ws.send 'foo'
        end

        Thread.stop
    end
end

编辑 2

我取得了进一步的进展。我现在使用Async Sinatra gem,它需要Thin web 服务器。它是这样设置的:

require 'sinatra'
require 'sinatra/async'
require 'websocket-client-simple'

set :server, 'thin'

register Sinatra::Async

aget '/' do
    puts 'Request received'

    socketResponse('wss:// ... URL ...')
end

def socketResponse(url)
    ws = WebSocket::Client::Simple.connect(url)

    puts 'Connected to web socket'

    ws.on :message do |message|
        puts 'Got message:' + message.to_s
    end

    ws.on :close do
        puts 'WS closed'
        body 'Closed ...'
    end

    ws.on :open do
        puts 'WS open'

        message = 'A nice message to process'
        ws.send message
        puts 'Sent: ' + message
    end
end

问题是,它仍然无法正常工作。它的控制台输出符合预期:

Request received
Connected to web socket
WS open
Sent: A nice message to process
Got message: blah blah blah
WS closed

但它没有将任何数据发送回客户端。 body 'Closed ...' 方法似乎没有任何效果。

【问题讨论】:

  • 旁注:您的问题中建议的设计适得其反并且性能很重。只要您的应用程序运行,保持 websocket 连接始终处于打开状态会更有意义。这就是 websockets 的全部意义——维持持久连接。
  • 这不是我的真实代码,而只是我能想到的用于演示问题的最简单的编写方式。但是谢谢,这是一个很好的提示。
  • 澄清一下,您不希望页面在 Web 套接字代码运行之前加载?你还说它应该是异步的,所以我很困惑。您可以在路由中使用 async 函数,因为路由将在没有来自 async 方法的任何信息的情况下返回。
  • 页面的内容取决于 web socket 服务器发送的内容,所以在 WS 代码运行之前无法加载页面。它只需要异步,因为 WS 是异步的。它必须异步等待 WS 消息,但上面的函数 socketResponse() 应该阻塞,直到 WS 关闭。这就是问题所在。
  • 为什么不把web socket连接卸载到客户端,让客户端直接使用web socket连接请求和处理数据?

标签: rest asynchronous websocket sinatra thin


【解决方案1】:

问题在于async-sinatra 使用自己的线程,websocket-client-simple 也是如此。解决方案是使用绑定和eval 函数,尽管这根本不是很有效。我希望有优化或更好的解决方案。

require 'sinatra'
require 'sinatra/async'
require 'websocket-client-simple'

set :server, 'thin'

register Sinatra::Async

aget '/' do
    puts 'Request received'

    socketResponse('wss:// ... URL ...', binding)
end

def socketResponse(url, b)
    ws = WebSocket::Client::Simple.connect(url)

    puts 'Connected to web socket'

    ws.on :message do |message|
        puts 'Got message:' + message.to_s
    end

    ws.on :close do
        puts 'WS closed'
        EM.schedule { b.eval " body 'Closed' " }
    end

    ws.on :open do
        puts 'WS open'

        message = 'A nice message to process'
        ws.send message
        puts 'Sent: ' + message
    end
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-15
    • 1970-01-01
    • 2021-11-15
    • 2011-11-10
    • 2019-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多