I am working on socket programming and I am new to socket. I got stuck. I am able to send the message on server easily but not able to get the message from the server. This could mean that my socket.on() method not working.
My socket connection code is
-(void)socketSession
{
NSString *strURl = [NSString stringWithFormat:URl];
NSURL* url = [[NSURL alloc] initWithString:strURl];
socket = [[SocketIOClient alloc] initWithSocketURL:url options:#{#"log": #YES, #"forcePolling": #YES}];
[socket connect];
[self recieveServerMessage];
[socket on:#"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
NSLog(#"socket connected");
}];
}
I call this method on viewDidLoad in viewController class and recieveServermessage method is
-(void)recieveServerMessage{
NSLog(#"receiving...message...");
[socket on:#"chat" callback:^(NSArray* data, SocketAckEmitter* ack) {
NSLog(#"recieveMessage");
}];
}
My control don't come when message is sent by server.
and below format is given by web server client
socket.on(":chat", function(data) {
// fired when any chat message is raised
// data.to_id = my _id. Please verify this before processing the packet
// data.sender_id = sender's _id. Map against counsellor list API or /api/getUserName?_id=(user's _id)
// data.msg = text message sent by the sender
})
Now please guide me. I have been stuck on this the last two days.
Thanks
Full code of socket io javascript class will be helpful.
You need to write emit function to send message from server to client. Like:
sockets.emit('chat', message);
And from client to server:
[socket emit:#"chat" withItems:#[#"message", #"other data"]];
I found my solution.Main problem was in socket handshake. Now I am using
NSURL* url = [[NSURL alloc] initWithString:#"https://unbottle.me"];
socket = [[SocketIOClient alloc] initWithSocketURL:url
options:#{#"connectParams" : #{#"_id": userID,#"isCounsellor": #NO,#"counsellor_id": counsellorID}}];
Earlier I was not using #"connectParams" key value in dictionary so I was facing this issue.
Thanks
Related
I'm very new to Objective-C and would like to communicate between an iOS app which I'm working on and my Python 3 socket server (which works). The problem is I don't know how to use sockets in Objective-C and don't know where to start when installing libraries in Xcode 8. For now I just want to be able to send data to the server and receive a response back on the iOS device. I have seen sys.socket.h but again, I don't know how to use it, any help would be much appreciated.
Thanks!
Few years ago I used SocketRocket. I am posting some of the code of it from my old project. I don't know if that still works but you might get the idea of using it.
Connecting to server
NSMutableURLRequest *pushServerRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"ws://192.168.1.1"]];
[pushServerRequest setValue:#"WebSocket" forHTTPHeaderField:#"Upgrade"];
[pushServerRequest setValue:#"Upgrade" forHTTPHeaderField:#"Connection"];
[pushServerRequest setValue:"somekey" forHTTPHeaderField:#"Sec-WebSocket-Protocol"];
SRWebSocket *wsmain = [[SRWebSocket alloc] initWithURLRequest:pushServerRequest]; //Declare this as a global variable in a header file
wsmain.delegate=self;
[wsmain open];
Delegate Methods
-(void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message
{
NSLog(#"Message %#",message);
}
- (void)webSocketDidOpen:(SRWebSocket *)webSocket
{ NSLog(#"Connected");
}
Sending Commands
[wsmain send:commands];
By sending commands, you will receive a response in didReceiveMessage method.
have you seen https://github.com/robbiehanson/CocoaAsyncSocket?
It's easy to use socket
NSString *host = #"10.70.0.22";
int port = 1212;
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
NSError *error = nil;
[_socket connectToHost:host onPort:port error:&error];
if (error) {
NSLog(#"%#",error);
}
delegate
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
NSLog(#"success");
}
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
if (err) {
NSLog(#"error %#",err);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self connectToServer];
});
}else{
NSLog(#"disconnect");
}
}
Another option is socket.io. It has server libraries and a iOS client library written in Swift. It's API is quite extensive.
I have been stuck on this all day. I have the very simple ActionCable example app (the chat app) by David Heinemeier Hansson working correctly (https://www.youtube.com/watch?v=n0WUjGkDFS0).
I am trying to hit the websocket connection with an iPhone app. I am able to receive pings when I connect to ws://localhost:3000/cable, but I'm not quite sure how to subscribe to channels from outside of a javascript context.
Oh man, I went through this problem too after reading this question.
After a while, I finally found this magical Github issue page:
https://github.com/rails/rails/issues/22675
I do understand that this patch would break some tests. That is not
surprising to me. But the original issue I believe is still relevant
and shouldn't be closed.
The following JSON sent to the server should succeed:
{"command": "subscribe","identifier":{"channel":"ChangesChannel"}}
It does not! Instead you must send this:
{"command":
"subscribe","identifier":"{\"channel\":\"ChangesChannel\"}"}
I finally got the iOS app to subscribe to room channel following the Github user suggestion about Rails problem.
My setup is as follow:
Objective C
Using PocketSocket framework for making web socket connection
Rails 5 RC1
Ruby 2.2.4p230
I assume you know how to use Cocoapods to install PocketSocket.
The relevant codes are as follow:
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");
}
Important Note:
I also digged a bit into the subscription class source code:
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
Notice the line:
connection.server.channel_classes[id_options[:channel]]
We need to use the name of the class for the channel.
The DHH youtube video uses "room_channel" for the room name but the class file for that channel is named "RoomChannel".
We need to use the class name not the instance name of the channel.
Sending Messages
Just in case others want to know how to send messages also, here is my iOS code to send a message to the server:
-(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];
}
This assumes you've hooked up your UITextField to handle pressing the return key or some "send" button somewhere on your UI.
This whole demo app was a quick hack, obviously, if I was to do it in a real app, I would make my code more cleaner, more reusable and abstract it into a class altogether.
Connecting to Rails server from real iPhone device:
In order for iPhone app to talk to Rails server on real device, not iPhone simulator.
Do the following:
Check your computer's TCP/IP address. On my iMac for example, it might be 10.1.1.10 on some days (can change automatically in the future if using DHCP).
Edit your Rail's config > environment > development.rb file and put in the following line somewhere like before the end keyword:
Rails.application.config.action_cable.allowed_request_origins = ['http://10.1.1.10:3000']
Start your Rails server using following command:
rails server -b 0.0.0.0
Build and run your iPhone app onto the iPhone device. You should be able to connect and send messages now :D
I got these solutions from following links:
Request origin not allowed: http://localhost:3001 when using Rails5 and ActionCable
Rails 4.2 server; private and public ip not working
Hope that helps others in the future.
// open socket connection first
var ws = new WebSocket("ws://localhost:3000/cable");
// subscribe to channel
// 'i' should be in json
var i = { 'command': 'subscribe', 'identifier': {'channel':'ProfileChannel', 'Param_1': 'Value_1',...}};
ws.send(i);
// After that you'll receive data inside the 'onmessage' function.
Cheers!
Actually, Here is the code snippet that i'm using to connect to action cable.
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;
};
}
I'm developing an application which is connecting to another device and in order to connect to it my app has to find the IP of it. I'm doing it through UDP socket. I'm sending a message to the server application in the other device and then the server app sends me another message but after I receive it I only use the IP address. Then I want to get it from this .m file to another .m file which is going to make the TCP connection between the two devices. Here's the code I use:
NSString *SearchCommand = #"AT+S";
static int receivedByteCount = 0;
BOOL connectedThroughUDP;
- (void) activate {
connectedThroughUDP = NO;
AsyncUdpSocket *udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
[udpSocket bindToPort:1234 error:nil ];
[udpSocket enableBroadcast:YES error:nil];
NSData* data=[SearchCommand dataUsingEncoding:NSUTF8StringEncoding];
if ([udpSocket sendData:data toHost:#"255.255.255.255" port:30100 withTimeout:3 tag:0])
{
//2 second timeout. onUdpSocket methods will provide results
[udpSocket receiveWithTimeout:2 tag:0];
NSLog(#"Connected.");
}
}
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{
NSString *receiveddata = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
addressOfServer = host;
NSLog(#"addressOfServer = %#", addressOfServer);
connectedThroughUDP = YES;
return YES;
}
- (NSString *) getHost {
return addressOfServer;
}
- (BOOL) isItConnected {
return connectedThroughUDP;
}
addressOfServer is global variable.
In the Log when I want NSLog(#"addressOfServer = %#", addressOfServer); I receive the IP address which I want but then when I want to access it from the getHost method I receive (null).
I know it will be something very simple but it caused me a real headache in the past 3 hours so I would be very thankful if you can help me!
Are you calling these 3 lines right after each other like so:
UDPConnection *udpConnection = [[UDPConnection alloc] init];
[udpConnection activate];
host = [udpConnection getHost];
If so your issue is that [udpConnection activate]; is going to take some time to connect and figure out the address. You are calling [udpConnection getHost]; too soon.
You will need to setup a delegate or completion block that will be triggered when didReceiveData is fired
I am using GCDAsyncUdpSocket to write a UDP socket in my app. The scenario is like this: when users click the button, it will send a broadcast packet in LAN then listen to the response, there is a server in LAN which will respond with one UDP packet. When the app receives the response, it will do something.
I set GCDAsyncUdpSocket as followings:
- (void)setupSocket
{
_udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
if (![_udpSocket bindToPort:18686 error:&error]) {
NSLog(#"Error binding: %#",error);
return;
}
if (![_udpSocket beginReceiving:&error]) {
NSLog(#"Error receiving: %#",error);
return;
}
if (![_udpSocket enableBroadcast:YES error:&error]) {
NSLog(#"Error enableBroadcast: %#",error);
return;
}
}
then i send Packet in button action as following:
NSString *host = #"255.255.255.255";
int port = 8585;
NSString *msg = #"Hello from iOS";
NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
[_udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:0];
in
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
method i listen the port to do somethings. It works perfectly at beginning, but if you try to click the button later (about 1 hour), then it cannot send UDP packet any more.
My server in LAN will print the data received. I thought there was something wrong with send method. so i use BSD socket methods to send The data. and use GCDAsyncUdpSocket to receive the response. but the same thing happened after a while. this time i can send but cannot receive.
Am i missing something about GCDAsyncUdpSocket? why it cannot send/receive after a while? Any help would be much appreciated.
It may be some timeout setting. Implement the GCDAsyncUdpSocketDelegate protocol to fetch detailed information about what is going on.
The hard solution is to establish a new connection.
I really need some help with my project...
I need to exchange data with my server written in Java. I tried using GCDAsyncSocket, and I can send message to server, read it on server, but when server sends response to client, I can't (don't know how to) read it on client. Here is part of my code:
- (void) someMethod{
NSError *err = nil;
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
if(![asyncSocket connectToHost:#"localhost" onPort:7777 error:&err]){
// If there was an error, it's likely something like "already connected" or "no delegate set"
NSLog(#"I goofed: %#", err);
}
NSString *requestStr = #"<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><root><service>1</service><type>1</type><userProperties><username>ivo</username></userProperties></root>";
NSData *requestData = [requestStr dataUsingEncoding:NSUTF8StringEncoding];
[asyncSocket writeData:requestData withTimeout:-1.0 tag:0];
[asyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:1.0 tag:0];
[asyncSocket disconnectAfterWriting];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
if (tag == 0)
NSLog(#"First request sent");
else if (tag == 2)
NSLog(#"Second request sent");
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",str);
}
Please help, if there is another way I am willing to try as I am getting desperate...
I see that you're sending XML, with no particular terminator at the end of your request data, yet you're expecting the server to send a response terminated by a \r\n?
What does the protocol specify?
Sending and receiving data over tcp is a common cause of confusion because tcp is stream based. It has no concept of individual reads/writes. It treats all data as conceptually a never ending stream. The protocol dictates message boundaries. For a better explanation, see the "Common Pitfalls" article from GCDAsyncSocket's wiki:
https://github.com/robbiehanson/CocoaAsyncSocket/wiki/CommonPitfalls
I think it will help explain a lot.