【问题标题】:Connecting to ActionCable from iOS app从 iOS 应用程序连接到 ActionCable
【发布时间】:2016-05-10 18:48:16
【问题描述】:

我一整天都被困在这上面。我有一个由 David Heinemeier Hansson 编写的非常简单的 ActionCable 示例应用程序(聊天应用程序)工作正常 (https://www.youtube.com/watch?v=n0WUjGkDFS0)。

我正在尝试与 iPhone 应用程序建立 websocket 连接。当我连接到ws://localhost:3000/cable 时,我能够接收到 ping,但我不太确定如何从 javascript 上下文之外订阅频道。

【问题讨论】:

  • 我正在尝试做同样的事情,但没有成功。你在客户端 IOS 部分使用了哪些代码?
  • 你有想过这个吗?

标签: ruby-on-rails websocket ruby-on-rails-5 actioncable


【解决方案1】:

哦,伙计,读完这个问题后,我也遇到了这个问题。

折腾了一阵子,终于找到了这个神奇的 Github issue page:

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

我明白这个补丁会破坏一些测试。那不是 令我惊讶。但我认为最初的问题仍然相关 并且不应关闭。

以下 JSON 发送到服务器应该会成功:

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

没有!相反,您必须发送:

{“命令”: "订阅","标识符":"{\"频道\":\"ChangesChannel\"}"}

根据 Github 用户关于 Rails 问题的建议,我终于让 iOS 应用订阅房间频道。

我的设置如下:

  • 目标 C
  • 使用 PocketSocket 框架进行网络套接字连接
  • Rails 5 RC1
  • Ruby 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 上某处的一些“发送”按钮。

整个演示应用程序是一个快速的 hack,显然,如果我要在一个真正的应用程序中完成它,我会让我的代码更简洁、更可重用并将其抽象为一个类。

从真实的 iPhone 设备连接到 Rails 服务器:

为了让 iPhone 应用与真实设备上的 Rails 服务器通信,而不是 iPhone 模拟器。

执行以下操作:

  1. 检查计算机的 TCP/IP 地址。例如,在我的 iMac 上,有时可能是 10.1.1.10(如果使用 DHCP,将来会自动更改)。
  2. 编辑 Rail 的 config &gt; environment &gt; 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

我从以下链接获得了这些解决方案:

Request origin not allowed: http://localhost:3001 when using Rails5 and ActionCable

Rails 4.2 server; private and public ip not working

希望将来能帮助其他人。

【讨论】:

  • 是否有任何取消这些订阅的注意事项?
  • 如果我想断开连接,我可以简单地断开连接,还是需要向 ActionCable 发送一条消息以正确清理此连接?
【解决方案2】:

//先打开socket连接

var ws = new WebSocket("ws://localhost:3000/cable");  

//订阅频道
// 'i' 应该在 json 中

var i = { 'command': 'subscribe', 'identifier': {'channel':'ProfileChannel', 'Param_1': 'Value_1',...}};

 ws.send(i);

// 之后,您将在“onmessage”函数中接收数据。

干杯!

【讨论】:

  • 这是 Javascript 还是 WebSocket 来自哪个库?
【解决方案3】:

实际上,这是我用来连接动作电缆的代码 sn-p。

  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;
         };
      }

【讨论】:

  • 你能分享你的路线吗?
  • 你好。我试过但没有收到这里的广播是我的 JS ``` ws.onopen = (data) => { const i = JSON.stringify({ command: 'subscribe', identifier: JSON.stringify({ channel: ' polls_channel' }) }); // 发送数据请求 const response = ws.send(i); //eslint-disable-line }; ws.onmessage = ((message) => { if (JSON.parse(message.data).type === 'change') { fetchPolls(); } }); ```
  • 要显示您的接收数据,请使用以下代码 ws.onmessage = function (evt) { var received_msg = evt.data; var json_obj = JSON.parse(received_msg); console.log("Rcvd",json_obj["message"]) };
猜你喜欢
  • 2014-04-12
  • 1970-01-01
  • 2014-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多