AsyncSockets - ReadToData - Doesn't work like expected - iphone

It's my first specific question here on stackoverflow, cause I couldn't found any helpful solutions for my problem yet.
I need a low level socket connection between my iPhone and OSX Workstation (as TCP Server), to interchange some media data like pictures or audio files. So I think AsyncSockets is a good choise to get this to work. I've often used it for some tiny byte communication.
My Problem is, that I want to use a kind of a header/protocol to tell the server how much data bytes are still in pipe.
A simple communication like "hello world" is working fine, so there are no connection problems.
The mobile device (that wants to send a picture) does the following.
[self setHost:#"172.22.42.207"];
self.socket = [[[AsyncSocket alloc] initWithDelegate:self] autorelease];
NSError *err = nil;
[[self socket] connectToHost:self.host onPort:5009 error:&err];
...
NSData *t = UIImagePNGRepresentation(test);
NSString *header = [NSString stringWithFormat:#"%i", t.length];
NSMutableData *headerData = [[header dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
[headerData appendBytes:[AsyncSocket CRLFData] length:[[AsyncSocket CRLFData] length]];
[[self socket] writeData:headerData withTimeout:-1 tag:0];
The server is listening that way:
AsyncSocket *s = [[AsyncSocket alloc] initWithDelegate:self];
NSError *err = nil;
[s acceptOnPort:5009 error:&err];
if(err)
NSLog(#"EPIC FAIL...\n%#", err);
....
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[newSocket readDataToData:[AsyncSocket CRLFData] withTimeout:-1 tag:0];
}
Now: If I use readData:withTimeout:tag it all works like a charm. But once I change the code to readDataToData:withTimeout:tag, to split the header from the other content, the onSocket:didConnectToHost:port: method is never called. Here are some pretty function logs (I placed them in every delegate method)
Client side:
2012-01-31 13:40:32.962 AVMobile[20643:10703] -[SLViewController onSocket:didConnectToHost:port:]
2012-01-31 13:40:32.964 AVMobile[20643:10703] -[SLViewController onSocket:didWriteDataWithTag:]
Server side:
2012-01-31 13:40:32.961 AVServer[20618:707] -[SLAppDelegate onSocket:didAcceptNewSocket:]
So, next idea... just compare the sending and receiving bytes, so:
Sending: <33333736 35365cba>
Receiving: <33333736 35365cba>
Yeah... now my final question: What am I doing wrong!?
Why isn't it working out for me :)?
Greetings & thanks!
sniperosx

Found a solution:
Just don't use -1 as timeout.
With -1 timeout the AsyncSocket is reading data until the other side is closing the connection, so in this range no delegate method is called.
Cheerz
sniperosx
[Closed]

Related

No Writing posible: SSL / TLS with AsyncSocket for iOS

I am working with the AsyncSocket for iOS. (https://github.com/robbiehanson/CocoaAsyncSocket)
I wrote my own Class to handle the connection with AsyncSocket. I wrote the method writeDate: (NSString *) data and add the AsyncSocket Callbackfunctions in my Class.
-(void)writeData: (NSString*) data
{
NSString *test = [NSString stringWithFormat:#"test"];
NSData *raw = [test dataUsingEncoding:NSUTF8StringEncoding];
[socket writeData:raw withTimeout:-1 tag:1];
}
The connection works great and if I'm trying to send a message in "didConnectToHost" after [sock startTLS:setting] it works.
But if I try to send a message with my own method (writeDate: (NSString *) data) and the call:
MyClass *test = [MyClass sharedSSLConnection];
[test writeData:#"test"];
It failed. I think the problem is, that the Handshake isn't ready when the method want's to sent a message on the socket. But how can I solve this? I did not find any solution.
Thanks for your help!
Might recommend that your MyClass store the data and wait until the state is ready onSocketDidSecure and send it then?

nsurlconnection asynchronous request

First of all the questions are failry simiple.. if you just want to see what they are skip to the bottom of this post and you will see them in bold.. for more detail then you can read the rest of this post...
I am just trying to iron out my NSURLConnection so that its working smoothly and I understand this properly. There is a profound lack of example/tutorials for Asynchronous connections on the internet or not any that I can find that explaine what is going on with any level of depth other than getting the connection up and running which after working on it seems pretty simple. Hopefully this question can full the void that I feel is out there for other users.
So, in my .h file i have imported the foundations headers and declared the methods required for the received or lack of received data (errors etc).
.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h> //add foundations
//.. other headers can be imported here
#interface MyViewController: UITableViewController {
//Im not setting any delegates to access the methods because Is all happening in the same
//place so I just use the key word 'self' when accessing the methods declared below
//I'm not sure if this is the best thing to do but I wasn't able to get my head around declaring the delegate or how it would help me with the way I have set up my request etc.
}
- (IBAction)setRequestString:(NSString *)string; //this method sets the request and connection methods
//these methods receive the response from my async nsurlconnection
- (void)receivedData:(NSData *)data;
- (void)emptyReply;
- (void)timedOut;
- (void)downloadError:(NSError *)error;
So thats my header file.. pretty simple not much explaining needed.
.m
//call setRequestString from some other method attached to a button click or something
[self setRequestString:#"rss.xml"];
//..
- (IBAction)setRequestString:(NSString *)string
{
//Set database address
NSMutableString *databaseURL = [[NSMutableString alloc] initWithString:#"http:www.comicbookresources/feeds/"]; // address not real jsut example
//append the string coming in to the end of the databaseURL
[databaseURL appendString:string];
//prepare NSURL with newly created string
NSURL *url = [NSURL URLWithString:databaseURL];
//AsynchronousRequest to grab the data
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil){
[self receivedData:data];
}else if ([data length] == 0 && error == nil){
[self emptyReply];
}else if (error != nil && error.code == NSURLErrorTimedOut){ //used this NSURLErrorTimedOut from foundation error responses
[self timedOut];
}else if (error != nil){
[self downloadError:error];
}
}];
}
now set up the methods that were initialized in the .h file and called in the if statement above
- (void)receivedData:(NSData *)data
{
NSString* newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", newStr); //logs recived data
//now you can see what data is coming in on your log
//do what you want with your data here (i.e. start parsing methods
}
- (void)emptyReply
{
//not sure what to do here yet?
}
- (void)timedOut
{
//also not sure what to do here yet?
}
- (void)downloadError:(NSError *)error
{
NSLog(#"%#", error);
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error!" message:#"A connection failure occurred." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[errorAlert show];
}
Cool so that pretty much the basics of what I have done right there.. now the questions I have are as follows.
Question one:
Where I call NSURLConnection like so
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
What is happening here what is the ^ for is that executing that whole block (including the if statements) on a different thread or something? because it looks alot like grand central dispatch formatting but slightly different.
Question two:
what should I be doing inside emptyReply & timedOut methods?
Question three:
How would I incorporate caching into this? I would like to cache the responses I get back from different requests. i.e. with my setRequestString you will see there is a string input parameter, so i can request different rss feeds with the same method.. I need to figure out how to cache these responses into individual caches.. but im not sure where to start with it.
Finally
If you have made it this far, thank you very much for reading my question. Hopefully with your responses we can get a pretty nice solution going here.. that other people can use for themselves and pick and choose the bits and peices they need that works for there own solution..
Anyway thank you very much for reading and I look forward to your replies.. even if they are just refrences to tutorials or examples you think might help me.. anything is good I just want to fully understand whats going on and whats a good solution.
Read about blocks in Apple documentation. Its new. Or you can read here
You can show errors such as request timed out etc. You don't really have to handle them separately than the error one unless you have special logic.
Try this for caching
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:TIMEOUT_INTERVAL];

Little problem with AsyncUdpSocket receiving data after connecting to broadcast instead of server

I have a problem with AsyncUdpSocket.
I used to connect to a server, send some data and get some response. Now since I do not know the actual address of the server I had to change my code and send the data to the broadcast address 255.255.255.255.
Here is my code :
NSString *bchost = #"255.255.255.255";
NSString *host = #"10.1.0.1";
int udpPort = 6001;
AsyncUdpSocket *udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
[udpSocket bindToPort:udpPort error:nil];
[udpSocket enableBroadcast:YES error:nil];
NSError *error = nil;
if ([udpSocket connectToHost:bchost onPort:udpPort error:&error])
{
[udpSocket receiveWithTimeout:10 tag:0];
[self sendToUDPServer:#"HELLO"];
}
So, the problem is that it works with "host" but not with "bchost". On both cases I see on the server side that the data is received and the answer is sent to the address of the sender (which should be the iOS device) but on the device I do not get the data when I send it to bchost.
Any idea what I am missing ?
Ok, unfortunately all reply's do not work for me but I found the solution, finally ;)
NSString *bcHost = #"255.255.255.255";
NSString *anyHost = #"0.0.0.0";
int UDP_SOCKET_PORT = 6001;
int DISCOVERY_PORT = 6003;
udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
[udpSocket bindToAddress:anyHost port:DISCOVERY_PORT error:nil];
[udpSocket enableBroadcast:YES error:nil];
[udpSocket receiveWithTimeout:10 tag:0];
[udpSocket sendData:[#"Hello" dataUsingEncoding:NSASCIIStringEncoding] toHost:bcHost port:UDP_SOCKET_PORT withTimeout:-1 tag:0];
If there is an server behind it, it will trigger a response and this will also allow to get the ip from the server for further processing.
You are 'connecting' to the host. Per the Unix socket FAQ, this means you'll only get UDP packets back that have a source IP address of 255.255.255.255. Connecting establishes a 1-to-1 relationship for UDP, such that received packets whose source addresses differ from the connected address will be filtered.
If you don't connect (you'll have to change your send line to target the broadcast address), it should work. You'll send toHost:bcHost -- and then your receive should get all packets destined for its port.
Based on Arthur's answer, here's the working code. I'm wondering if receive should start before the send, just to make sure we don't miss a very fast reply before receive is ready, but so far it doesn't seem necessary in my situation. Also, reference this post on how to create receiving methods.
AsyncUdpSocket *udpSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
[udpSocket bindToPort:1234 error:nil ];
[udpSocket enableBroadcast:YES error:nil];
NSData* data=[messageToSend dataUsingEncoding:NSUTF8StringEncoding];
if ([udpSocket sendData:data toHost:#"255.255.255.255" port:4321 withTimeout:3 tag:0])
{
//2 second timeout. onUdpSocket methods will provide results
[udpSocket receiveWithTimeout:2 tag:0];
}
I could be completely crazy, but it seems like this is a standing issue with AsyncUdpSocket on iOS.
There's several error reports similar and even identical to yours on their Google Code page; people have complained that they are unable to receive Udp packets after broadcasting, and in some cases, even at all.
http://code.google.com/p/cocoaasyncsocket/issues/list?can=2&q=AsyncUdpSocket+receive

writeData on a syncsocket always blocks on iPhone

I use the asyncsocket sample as a starting point to learn more about wlan communication on iPhone.
On the Mac I start a sample server opening port 0. This works, since I can write data with a test client running on the mac.
On the iPhone I think I managed to connect since "streams connected" returns YES.
Then I would like to send data with a syncsocket: (EDITED VERSION WITH COMPLETE CODE)
import "InterfaceTestAppDelegate.h"
import "InterfaceTestViewController.h"
import "AsyncSocket.h"
import "SyncSocket.h"
#implementation InterfaceTestAppDelegate
#synthesize window;
#synthesize viewController;
(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)remoteHost port:(UInt16)remotePort
{
NSLog(#"Socket is connected!");
NSLog(#"Remote Address: %#:%hu", remoteHost, remotePort);
NSString *localHost = [sock localHost];
UInt16 localPort = [sock localPort];
NSLog(#"Local Address: %#:%hu", localHost, localPort);
}
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(#"application:didFinishLaunchingWithOptions:");
/*
asyncSocket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *err = nil;
if (![asyncSocket connectToHost: #"192.168.0.30" onPort: 1234 error: &err])
{
NSLog(#"Error connecting: %#", err);
}
NSData *data = [#"testxyz" dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"trace 1");
[asyncSocket writeData:data withTimeout:10 tag:0];
NSLog(#"trace 2");
*/
syncSocket = [[SyncSocket alloc] initWithTimeout: 10];
syncSocket.nsLog = YES;
if (![syncSocket connectToHost: #"192.168.0.30" onPort: 12345])
{
NSLog(#"Error connecting syncSocket:");
}
NSData *data = [#"testxyz" dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"syncSocket trace 1");
[syncSocket writeData:data];
NSLog(#"syncSocket trace 2");
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
It never continues to send the data, the writeData always blocks.
The IP 192.168.0.30 is my Mac's IP. I just used any port 12345 now as you suggested above.
But I don't really know what I have to do on the Mac to receive??
As you can see I actually use syncsocket, then it blocks.
I also tried asyncSocket, then I get the message in the asyncsocket class: writeStream Can NOT Accept Bytes
Maybe its that I don't setup the Mac correctly,ie what app do I need to run on the Mac to test?
Many thank!
For what it's worth, this is specifically how you typically read in some data using AsyncSocket:
-(void)onSocket:(AsyncSocket *)sock
didReadData:(NSData*)data withTag:(long)tag
{
[data getBytes:&getMe length:sizeof(CommProt)];
// now, you must roll in the next read...
[sock readDataToLength:sizeof(CommProt) withTimeout:-1 tag:0];
// CommProt is your communications protocol, so sizeof(CommProt)
// is how much to read at a chunk.
// you can now simply access the fields of getMe,
// for example getMe.x, getMe.y, getMe.latestValue etc etc.
// hope it helps!
}
Of course, you would have previously rolled in the first "primer" read command:
You do that when you connect to a host, hence:
-(void)onSocket:(AsyncSocket *)sock
didConnectToHost:(NSString *)host port:(UInt16)port
{
if ( yourAppSaysItsOkToConnectAtThisMoment == NO )
{
[sock disconnect]; // (so easy, AsyncSockets is a masterpiece)
return;
}
// .. blah blah
// the critical 'primer' read command
[sock readDataToLength:sizeof(CommProt) withTimeout:-1 tag:0];
// .. blah blah
}
Don't forget you must roll in the next read in two places, (a) when you first connect and (b) of course, after each read!
In the example your communications protocol would look like this ...
typedef struct _CommProt // v.3
{
BOOL pressExplosionButton;
BOOL pressFireworksButton;
float usersSteering;
float usersTemperature;
float usersAltitude;
float usersAngle;
}
CommProt;
Variable like "getMe" in the example would simply look like this:
CommProt getMe;
CommProt sendMe;
If you are struggling to understand this type of communications protocol, also try this long answer:
Tablet(iPad/Android)-Server Communication Protocol
AsyncSocket is incredibly beautiful, it was written by the mysterious Justin Voss who seemed to drop off the internet after giving it to the world - it's one of the best libraries ever written, it's a masterpiece.
Hope it helps.

SSDP on the iPhone

I need to be able to send out a UDP message and also receive one in order to discover SSDP devices on the network from the iPhone.
I know that I need to send the packet to the multicast address and my HTTP request needs to look something like this:
M-SEARCH * HTTP/1.1
Host: 239.255.255.250:1900
Man: ssdp:discover
Mx: 3
ST: "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
From reading the docs it appears that I can do all this with CFNetwork and despite reading (and re-reading the docs) I am struggling to get started. Can anyone recommend and tutorials or code snippets to get me over the initial learning hump?
I've got the CFNetwork programming guide:
http://developer.apple.com/mac/library/documentation/Networking/Conceptual/CFNetwork/CFNetwork.pdf
and Beej's Guide to Network programming Using Internet Sockets:
http://beej.us/guide/bgnet/
Thanks
Dave
P.S.
I am unable to use any of the 3rd party libraries and frameworks in this instance.
I have used AsyncUdpSocket successfully to run SSDP Discovery and find controllers. Here are my code snippets:
Initialize and setup the socket:
// AsyncUdpSocket *ssdpSock = [[AsyncUdpSocket alloc] initWithDelegate:self];
AsyncUdpSocket *ssdpSock = [[AsyncUdpSocket alloc] initIPv4];
[ssdpSock setDelegate:self];
Note the first line commented out. I found on the AsyncUdpSocket forums some issues with duplicates. I don't think I was facing them but I did it anyhow.
I added error checking, and it was useful because during my debugging I wasn't closing sockets and I started getting socket setup failures:
NSError *socketError = nil;
if (![ssdpSock bindToPort:1900 error:&socketError]) {
NSLog(#"Failed binding socket: %#", [socketError localizedDescription]);
return statusController;
}
if(![ssdpSock joinMulticastGroup:#"239.255.255.250" error:&socketError]){
NSLog(#"Failed joining multicast group: %#", [socketError localizedDescription]);
return statusController;
}
if (![ssdpSock enableBroadcast:TRUE error:&socketError]){
NSLog(#"Failed enabling broadcast: %#", [socketError localizedDescription]);
return statusController;
}
[ssdpSock sendData:[self.discoverControllerString dataUsingEncoding:NSUTF8StringEncoding]
toHost:#"239.255.255.250"
port:1900
withTimeout:2
tag:1];
Notice the changes I have made to the time out. And then finally did the receive setup, and closed the socket. Note the socket close. Since I am in my own class when I am running this - the code above did not work for me.
[ssdpSock receiveWithTimeout: 2 tag:1];
[NSTimer scheduledTimerWithTimeInterval: 5 target: self
selector:#selector(completeSearch:) userInfo: self repeats: NO];
[ssdpSock closeAfterSendingAndReceiving];
The most important change probably was returning "NO" if I did not find my controller. The first receive was incidentally the discovery message itself coming back. And when I read through the AsyncUdpSocket.h file carefully - returning "NO" when it is not a packet you are looking for helped.
Also note that I am using ARC in my code but I compiled the AsyncUdpSocket without ARC support.
-(void) completeSearch: (NSTimer *)t
{
NSLog(#"%s",__FUNCTION__);
//[ssdpSock close];
//ssdpSock = nil;
}
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock
didReceiveData:(NSData *)data
withTag:(long)tag
fromHost:(NSString *)host
port:(UInt16)port
{
NSLog(#"%s %ld %# %d",__FUNCTION__,tag,host,port);
NSString *aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"%#",aStr);
NSString *compareString = [aStr stringByPaddingToLength:[self.responseString length] withString:#"." startingAtIndex:0];
//NSLog(#"%#", compareString);
//NSLog(#"%#", self.responseString);
if ([compareString isEqualToString:self.responseString])
{
NSLog(#"String Compare, Controller Found!");
[self.controllerList addObject:aStr];
//NSData *controllerIP = [aStr dataUsingEncoding:NSUTF8StringEncoding];
[[NSNotificationCenter defaultCenter] postNotificationName:#"DiscoveredController" object:nil];
return YES;
}
return NO;
}
I have the following code for SSDP search in my app:
-(void)discoverDevices {
ssdpSock = [[AsyncUdpSocket alloc] initWithDelegate:self];
[ssdpSock enableBroadcast:TRUE error:nil];
NSString *str = #"M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMan: \"ssdp:discover\"\r\nST: mydev\r\n\r\n";
[ssdpSock bindToPort:0 error:nil];
[ssdpSock joinMulticastGroup:#"239.255.255.250" error:nil];
[ssdpSock sendData:[str dataUsingEncoding:NSUTF8StringEncoding]
toHost: #"239.255.255.250" port: 1900 withTimeout:-1 tag:1];
[ssdpSock receiveWithTimeout: -1 tag:1];
[NSTimer scheduledTimerWithTimeInterval: 5 target: self
selector:#selector(completeSearch:) userInfo: self repeats: NO]; }
-(void) completeSearch: (NSTimer *)t {
NSLog(#"%s",__FUNCTION__);
[ssdpSock close];
ssdpSock = nil;}
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{
NSLog(#"%s %d %# %d",__FUNCTION__,tag,host,port);
NSString *aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"%#",aStr);}
It uses the AsyncUdpSocket from CocoaAsyncSocket.
OK, finally done it. Found a class in the public domain (thanks Chris) called AsyncUdpSocket that lets you create a UDP socket which you can then turn on broadcasting and join the multicast address.
There is a nice sendData method, complete with adding to a run loop to prevent blocking.
Hope that helps.
Dave