0

ここにチャット アプリケーションがあります。WebSocketsのwikiを調べていました。コードは次のように ChatController に記述されています。

class ChatController < WebsocketRails::BaseController
  def initialize_session
    # perform application setup here
    controller_store[:message_count] = 0
  end
end

私の質問:これをどのように実装する必要chatcontrollerがありApplicationControllerますか? . を使用するための新しいコントローラーを作成するかwebsockets、既存のchatcontrollerものを拡張する必要があるかを変更する必要がありWebsocketRailsますか? . 私は WebSockets を初めて使用するので、これに関するヘルプは本当に役に立ちます。

ありがとうございました 。

4

1 に答える 1

2

答えは、websocket-rails gemに関連して、Faye とPleziと同様に YES です。Rails で使用されるものとは異なる、新しいコントローラー クラスを作成する必要があります。

websocket-rails は を継承するWebsocketRails::BaseController必要がありますが、Rails コントローラーは継承する必要がありますActionController::Base(通常は、このクラスを継承する ApplicationController を継承することによって)。

Ruby は二重クラス継承をサポートしていません (ただし、モジュールを使用する場合は Mixin が可能です)。

一方、フェイはコントローラーを同じオブジェクト指向の方法で使用しておらず、より多くのオプションがあります。たとえば、websocket イベントをコントローラーの CLASS メソッドにマップすることはできますが、コントローラーの内部メカニズムの一部が壊れる可能性があるため、websocket 接続ごとに Rails コントローラーを初期化する際に問題が発生する可能性があります。たとえば、セッション情報は利用できず、おそらく Rails 固有のメソッドをすべて回避する必要があります。

Plezi では、これらの継承の問題は解消されませんが、Plezi は Controller が持つパブリック メソッドへの Http ルートを自動的に作成するため、Rails メソッドは意図しない方法で公開されます。一方、Plezi コントローラーは、Http と Websocket の両方に応答できます。

Faye と Plezi についても書いた理由は、2014 年 3 月に websocket-rails gem が最後に更新された (CHANGELOG によると) ためです ...

Rails の最新の更新にどれだけ耐えられるか (または生き残ったか) はわかりませんが、先に進むことをお勧めします。

2015 年 11 月現在、Faye がより一般的なオプションであり、Plezi はこの分野の新しいプレーヤーです。私は Plezi の作者です。

編集(コメントへの回答)

Faye と Plezi の両方で、あるユーザーが別のユーザーにメッセージを送信できるようにする必要があります。

Plezi の方が使いやすいと思いますが、これは私が Plezi を書いたからです。Faye を書いた人は、Faye の方が簡単だと思うでしょう。

チャットを実装する最良の方法については、希望する方法に応じていくつかのオプションがあります。

Plezi をインストールして (端末で) 実行すると、plezi アプリケーションのデモ コードを見ることができます。

  $ plezi mini my_chat

これは、既存のアプリケーションに Plezi Websocket ブロードキャストを追加するための簡単なハックです。

もし私があなたのデータベースにアクセスできたら、少し違ったやり方をしていたでしょう...しかし、概念実証としては十分です....今のところ.

に次の行を追加しますGemfile

gem 'plezi'

plezi_init.rbファイルを作成し、config/initializersフォルダーに追加します。これが今のところ保持されているものです(ほとんどはRails Cookieをハッキングしています。データベースにアクセスできず、フィールドを追加できないためです):

class WebsocketController

    def on_open
        # this is a Hack - replace this with a database token and a cookie.
        return close unless cookies[:_linkedchats_session] # refuse unauthenticated connections
        # this is a Hack - get the user
        @user_id = decrypt_session_cookie(cookies[:_linkedchats_session].dup)['warden.user.user.key'][0][0].to_s
        puts "#{@user_id} is connected"
    end

    def on_message data
        # what do you want to do when you get data?        
    end

    protected

    # this will inform the user that a message is waiting
    def message_waiting msg
        write(msg.to_json) if msg[:to].to_s == @user_id.to_s
    end

    # this is a Hack - replace this later
    # use a token authentication instead (requires a database field)
    def decrypt_session_cookie(cookie)
        key ='4f7fad1696b75330ae19a0eeddb236c123727f2a53a3f98b30bd0fe33cfc26a53e964f849d63ad5086483589d68c566a096d89413d5cb9352b1b4a34e75d7a7b'
        cookie = CGI::unescape(cookie)

        # Default values for Rails 4 apps
        key_iter_num = 1000
        key_size     = 64
        salt         = "encrypted cookie"         
        signed_salt  = "signed encrypted cookie"  

        key_generator = ActiveSupport::KeyGenerator.new(key, iterations: key_iter_num)
        secret = key_generator.generate_key(salt)
        sign_secret = key_generator.generate_key(signed_salt)

        encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON)
        encryptor.decrypt_and_verify(cookie)
    end
end

# IMPORTANT - create the Plezi, route for websocket connections
Plezi.route '/ws', WebsocketController

このアプリに必要な Plezi アプリケーションは、これでほぼすべてです。

ChatsController#createの直前に、次の行をメソッドに追加するだけrespond_toです。

 WebsocketController.broadcast :message_waiting,
          from: @msg.sender_id,
          to: @msg.receiver_id,
          text: @msg.text,
          msg: :chat

サーバーは以上です... さて、クライアントです。

次のスクリプトをchat.html.erbテンプレートに追加します (または、ターボリンクがスクリプトの初期化を台無しにする可能性があるため、スクリプトをapplication.jsファイルに追加します...ただし、ユーザーがログインするまで多くの接続を拒否することになります):

<script type="text/javascript">

// Your websocket URI should be an absolute path. The following sets the base URI.
// remember to update to the specific controller's path to your websocket URI.
var ws_controller_path = '/ws'; // change to '/controller/path'
var ws_uri = (window.location.protocol.match(/https/) ? 'wss' : 'ws') + '://' + window.document.location.host + ws_controller_path
// websocket variable.
var websocket = NaN
// count failed attempts
var websocket_fail_count = 0
// to limit failed reconnection attempts, set this to a number.
var websocket_fail_limit = NaN


function init_websocket()
{
    if(websocket && websocket.readyState == 1) return true; // console.log('no need to renew socket connection');
    websocket = new WebSocket(ws_uri);
    websocket.onopen = function(e) {
        // reset the count.
        websocket_fail_count = 0
        // what do you want to do now?
    };

    websocket.onclose = function(e) {
        // If the websocket repeatedly you probably want to reopen the websocket if it closes
        if(!isNaN(websocket_fail_limit) && websocket_fail_count >= websocket_fail_limit) {
            // What to do if we can't reconnect so many times?
            return
        };
        // you probably want to reopen the websocket if it closes.
        if(isNaN(websocket_fail_limit) || (websocket_fail_count <= websocket_fail_limit) ) {
            // update the count
            websocket_fail_count += 1;
            // try to reconect
            init_websocket();
        };
    };
    websocket.onerror = function(e) {
        // update the count.
        websocket_fail_limit += 1
        // what do you want to do now?
    };
    websocket.onmessage = function(e) {
        // what do you want to do now?
        console.log(e.data);
        msg = JSON.parse(e.data)
        alert("user id: " + msg.from + " said:\n" + msg.text)
    };
}
// setup the websocket connection once the page is done loading
window.addEventListener("load", init_websocket, false); 

</script>

終わり。

于 2015-11-04T17:59:03.863 に答える