Sinatra使用websocket客户端来响应http请求

时间:2017-08-31 18:18:42

标签: rest asynchronous websocket sinatra thin

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

  1. 请求从客户端进入我的Sinatra服务器
  2. 我的服务器打开外部服务器的Web套接字
  3. 我的服务器异步等待来自外部服务器的消息和东西,直到套接字关闭(这应该只需要大约200毫秒)。
  4. 我的服务器向客户端发送回复
  5. 我确定这不是太难完成,但我有点卡在上面。基本上,如果整个Web套接字逻辑可以包装在单个函数中,那么该函数可以被阻塞,并且就是这样。但我不知道如何包装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 ...'方法似乎没有任何效果。

1 个答案:

答案 0 :(得分: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