2017-08-31 119 views
9

我正在編寫一個Sinatra Web服務器,我想成爲RESTful,但事情是它必須與另一臺通過Web套接字進行通信的服務器進行交互。所以,這需要發生:Sinatra使用WebSocket客戶端來響應http請求

  1. 一個請求進入我的末日服務器從客戶
  2. 我的服務器打開一個網絡套接字連接到國外服務器
  3. 我的服務器異步等待來自國外的消息,事情服務器,直到關閉套接字(這應該只需要兩百元左右毫秒)
  4. 我的服務器返回給客戶端的響應

我敢肯定,這是不是太複雜的實現,但我有點卡住了。基本上,如果整個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 

EDIT 2

我已經取得了進一步進展。我現在使用的是Async Sinatra寶石,它需要Thin網絡服務器。這是它如何設置:

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

旁註:在您的問題建議的設計是適得其反,性能沉重。只要應用程序運行,保持WebSocket連接始終處於打開狀態更有意義。這就是websockets的重點 - 維持持續的連接。 – Myst

+0

這不是我真正的代碼,而只是寫出來的最簡單的方式,我可以想象它來演示問題。但是,謝謝你,這是一個很好的提示。 – tschwab

+0

只是爲了澄清,你不希望網頁加載後,直到網絡套接字代碼運行?你也說它應該是異步的,所以我很困惑。您可以在路由中使用異步函數,因爲路由將返回而不包含異步方法的任何信息。 – Cereal

回答

0

問題是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 
相關問題