18

私は一日中これにこだわっています。David Heinemeier Hansson による非常に単純な ActionCable サンプル アプリ (チャット アプリ) が正しく動作しています ( https://www.youtube.com/watch?v=n0WUjGkDFS0 )。

iPhoneアプリでwebsocket接続を打とうとしています。に接続すると ping を受信できますws://localhost:3000/cableが、javascript コンテキストの外部からチャネルをサブスクライブする方法がよくわかりません。

4

3 に答える 3

13

ああ、この質問を読んだ後、私もこの問題を経験しました。

しばらくして、ついにこの魔法のような Github の問題ページを見つけました。

https://github.com/rails/rails/issues/22675

このパッチによって一部のテストが中断されることは理解しています。それは私にとって驚くべきことではありません。しかし、私が信じている元の問題はまだ関連性があり、クローズされるべきではありません.

サーバーに送信される次の JSON は成功するはずです。

{"command": "subscribe","identifier":{"channel":"ChangesChannel"}}

そうではありません!代わりに、これを送信する必要があります:

{"command": "subscribe","identifier":"{\"channel\":\"ChangesChannel\"}"}

Railsの問題に関するGithubユーザーの提案に従って、ついにiOSアプリをルームチャンネルにサブスクライブしました。

私のセットアップは次のとおりです。

  • オブジェクティブ C
  • PocketSocket フレームワークを使用して Web ソケット接続を確立する
  • レール5 RC1
  • ルビー 2.2.4p230

Cocoapods を使用して PocketSocket をインストールする方法を知っていると思います。

関連するコードは次のとおりです。

ViewController.h

#import <PocketSocket/PSWebSocket.h>

@interface ViewController : UIViewController <PSWebSocketDelegate, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate>

@property (nonatomic, strong) PSWebSocket *socket;

ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self initViews];
    [self initConstraints];
    [self initSocket];
}

-(void)initSocket
{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"ws://localhost:3000/cable"]];

    self.socket = [PSWebSocket clientSocketWithRequest:request];
    self.socket.delegate = self;

    [self.socket open];
}

-(void)joinChannel:(NSString *)channelName
{
    NSString *strChannel = @"{ \"channel\": \"RoomChannel\" }";

    id data = @{
                @"command": @"subscribe",
                @"identifier": strChannel
                };

    NSData * jsonData = [NSJSONSerialization  dataWithJSONObject:data options:0 error:nil];
    NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

    NSLog(@"myString= %@", myString);

    [self.socket send:myString];
}

#pragma mark - PSWebSocketDelegate Methods -

-(void)webSocketDidOpen:(PSWebSocket *)webSocket
{
    NSLog(@"The websocket handshake completed and is now open!");

    [self joinChannel:@"RoomChannel"];
}

-(void)webSocket:(PSWebSocket *)webSocket didReceiveMessage:(id)message
{
    NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
    id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

    NSString *messageType = json[@"type"];

    if(![messageType isEqualToString:@"ping"] && ![messageType isEqualToString:@"welcome"])
    {
        NSLog(@"The websocket received a message: %@", json[@"message"]);

        [self.messages addObject:json[@"message"]];
        [self.tableView reloadData];
    }
}

-(void)webSocket:(PSWebSocket *)webSocket didFailWithError:(NSError *)error
{
    NSLog(@"The websocket handshake/connection failed with an error: %@", error);
}

-(void)webSocket:(PSWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean
{
    NSLog(@"The websocket closed with code: %@, reason: %@, wasClean: %@", @(code), reason, (wasClean) ? @"YES": @"NO");
}

重要な注意点:

また、サブスクリプション クラスのソース コードも少し掘り下げました。

def add(data)
        id_key = data['identifier']
        id_options = ActiveSupport::JSON.decode(id_key).with_indifferent_access

        subscription_klass = connection.server.channel_classes[id_options[:channel]]

        if subscription_klass
          subscriptions[id_key] ||= subscription_klass.new(connection, id_key, id_options)
        else
          logger.error "Subscription class not found (#{data.inspect})"
        end
      end

次の行に注意してください。

connection.server.channel_classes[id_options[:channel]]

チャネルのクラスの名前を使用する必要があります。

DHH の YouTube ビデオではルーム名に「room_channel」が使用されていますが、そのチャンネルのクラス ファイルの名前は「RoomChannel」です。

チャネルのインスタンス名ではなく、クラス名を使用する必要があります。

メッセージの送信

他の人がメッセージの送信方法も知りたい場合に備えて、サーバーにメッセージを送信するための iOS コードを次に示します。

-(void)sendMessage:(NSString *)message
{
    NSString *strMessage = [[NSString alloc] initWithFormat:@"{ \"action\": \"speak\", \"message\": \"%@\" }", message];

    NSString *strChannel = @"{ \"channel\": \"RoomChannel\" }";

    id data = @{
                @"command": @"message",
                @"identifier": strChannel,
                @"data": strMessage
                };

    NSData * jsonData = [NSJSONSerialization  dataWithJSONObject:data options:0 error:nil];
    NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

    NSLog(@"myString= %@", myString);

    [self.socket send:myString];
}

これは、UITextField をフックして、UI のどこかでリターン キーまたは何らかの「送信」ボタンを押すことを処理することを前提としています。

このデモ アプリ全体は簡単なハックでした。明らかに、実際のアプリでこれを行うとしたら、コードをよりクリーンで再利用可能にし、クラスにまとめて抽象化します。

実際の iPhone デバイスから Rails サーバーに接続する:

iPhoneアプリがiPhoneシミュレーターではなく、実際のデバイス上のRailsサーバーと通信するため。

以下をせよ:

  1. コンピュータの TCP/IP アドレスを確認してください。たとえば、私の iMac では、ある日は 10.1.1.10 になる場合があります (DHCP を使用している場合は、将来自動的に変更される可能性があります)。
  2. Rail のファイルを編集して、次の行をキーワードconfig > environment > development.rbの前などに追加します。end

    Rails.application.config.action_cable.allowed_request_origins = ['http://10.1.1.10:3000']

  3. 次のコマンドを使用して Rails サーバーを起動します。

    rails server -b 0.0.0.0

  4. iPhone アプリをビルドして iPhone デバイス上で実行します。これで接続してメッセージを送信できるはずです:D

次のリンクからこれらのソリューションを入手しました。

Rails5 と ActionCable を使用している場合、リクエスト元は許可されていません: http://localhost:3001

Rails 4.2 サーバー。プライベート IP とパブリック IP が機能しない

将来的に他の人に役立つことを願っています。

于 2016-05-21T08:39:11.473 に答える
0

実際、アクションケーブルに接続するために使用しているコードスニペットは次のとおりです。

  function WebSocketTest()
   {
         var ws = new WebSocket("ws://localhost:3000/cable");
         ws.onopen = function(data)
         {
           var i = JSON.stringify({"command":"subscribe" , "identifier": JSON.stringify({"channel":"CHANNEL_NAME"})});
           // send data request
           var j =  JSON.stringify({"command":"message","identifier": JSON.stringify({"channel":"CHANNEL_NAME"}),"data": {"message":"Hello World","action": "METHOD_NAME_IN_CHANNEL","email": "abc@xyz.com",  "token" : "xxxxxxxxxxxxx", "id": {"id_message" : "something", "ddd" : "something"}}}) 
           var response = ws.send(i);
            setTimeout(function()
            {
              var response1 = ws.send(j);
            }, 1000);
         };
         ws.onmessage = function (evt) 
         { 
            var received_msg = evt.data;
         };
      }
于 2016-05-22T13:54:43.380 に答える