从iOS应用程序连接到ActionCable

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

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

噢,看完这个问题后我也遇到了这个问题。

过了一会儿,我终于找到了这个神奇的Github问题页面:

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

我明白这个补丁会破坏一些测试。 这对我来说并不奇怪。 但我认为最初的问题仍然是相关的,不应该被关闭。

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

{“command”:“subscribe”,“identifier”:{“channel”:“ChangesChannel”}}

它不是! 相反,你必须发送:

{“command”:“subscribe”,“identifier”:“{\”channel \“:\”ChangesChannel \“}”}

在Github用户建议Rails问题后,我终于让iOS应用程序订阅了房间频道。

我的设置如下:

  • 目标C.
  • 使用PocketSocket框架进行Web套接字连接
  • Rails 5 RC1
  • Ruby 2.2.4p230

我假设您知道如何使用Cocoapods来安装PocketSocket。

相关代码如下:

ViewController.h

 #import  @interface ViewController : UIViewController  @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 youtubevideo使用“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应用程序在真实设备上与Rails服务器通信,而不是iPhone模拟器。

请执行下列操作:

  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

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

不允许请求源:http:// localhost:3001使用Rails5和ActionCable时

Rails 4.2服务器; 私人和公共IP无法正常工作

希望将来能帮助别人。

//首先打开套接字连接

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

//订阅频道
//’我’应该在json

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

//之后你会在’onmessage’函数里面收到数据。

干杯!

实际上,这是我用来连接动作电缆的代码片段。

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