从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模拟器。
请执行下列操作:
- 检查计算机的TCP / IP地址。 例如,在我的iMac上,某些天它可能是10.1.1.10(如果使用DHCP,将来可能会自动更改)。
-
编辑您的Rail的
config > environment > development.rb
文件,并在end
关键字之前的某处放入以下行:Rails.application.config.action_cable.allowed_request_origins = ['http://10.1.1.10:3000']
-
使用以下命令启动Rails服务器:
rails server -b 0.0.0.0
-
在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; }; }