Application Freezing when calling CocoaAsyncMethod scheduleDequeueRead - iphone

I'm using CocoaAsyncSocket library to create a TCP socket connection. The problem I'm having is after a few library methods are called, I'm getting an error.
appDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
socket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *error = nil;
if (![socket connectToHost:#"199.5.83.63" onPort:11005 error:&error])
{
NSLog(#"Error connecting: %#", error);
}
[socket readDataWithTimeout:10 tag:1];
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewController = [[[tekMatrixViewController alloc] initWithNibName:#"tekMatrixViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
And here are my methods from the CocoaAsyncSocket Library:
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length])];
NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
NSLog(#"RX length: %d", [data length]);
if(msg)
{
NSLog(#"RX:%#",msg);
}
else
{
NSLog(#"Fail");
}
}
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
NSLog(#"error - disconnecting");
//start reconnecting procedure here...
}
- (void)onSocketDidDisconnect:(AsyncSocket *)sock
{
NSLog(#"disconnected");
}
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
NSLog(#"connected");
}
When I run my app in the simulator, this is what my output log spits out:
2012-06-08 13:17:30.808 tekMatrix[2793:f803] connected
2012-06-08 13:17:30.815 tekMatrix[2793:f803] RX length: 8
2012-06-08 13:17:30.816 tekMatrix[2793:f803] Fail
After that, I get an error in the AsyncSocket.m file (part of library) in this method:
- (void)scheduleDequeueRead
{
if((theFlags & kDequeueReadScheduled) == 0)
{
theFlags |= kDequeueReadScheduled;
[self performSelector:#selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
}
}
Specifically, the error is on line:
[self performSelector:#selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
And the exception is: Thread 1: BAD_EXC_ACCESS (code 1=0, address=0xd0688b8a)
After that, the app is completely frozen in the simulator. If anybody could offer some insight as to why this is causing the app to freeze, I would really appreciate it.
Here is the library I'm using: CocoaAsyncSocket
EDIT:
After Enabling Zombie Objects and running the app in the simulator, this is spit out in the output:
2012-06-08 14:53:15.416 tekMatrix[3217:f803] *** -[__NSArrayI count]: message sent to deallocated instance 0x6d10a70
I'll have to do a little digging on this and figure out what's happening.
EDIT 2:
After a little digging using instruments, I found out the following:
An Objective-C message was sent to a deallocated object (zombie) at address: 0x6b8bb10.
EDIT 3:
Now the error reads:
Thread 1: EXC_BREAKPOINT(code=EXC_I386_BPT, subcode=0x0)
Here is a screenshot from instruments. I'm not really following how to interpret this, though. It looks like I'm sending a message to an object that has been deallocated. Is this true? If so, how do I go about figuring out where this occurs?
If the image is hard to see, here's a direct link: Instruments Screenshot

You are trying to access a object, that doesnt exsits anymore. Probably you are under-retaining/over-releasing it.
see this answer for a tool to find such objects: How do I set up NSZombieEnabled in Xcode 4?

I know it's way after the fact, but I just had this same exact problem. The solution was adding the ARC flag to AsyncSocket.m, -fobjc-arc
:)

Related

NSURLConnection doesn't receive data when creating many downloading objects in iOS5

I have been searching for this problem on the SOF for several days and I still have not found the solution (say the same problem) yet.
I'm making and app that downloads 5 images simultaneously in an URL list (each image is on a different server).
I have an ImageDownloader class subclasses NSOperation and implements the NSURLConnectionDataDelegate.
So that I can add an instance of ImageDownloader to an operationQueue in the ViewController and it will run in a separate thread under the operationQueue. The line that add the downloader to the operationQueue is here:
downloader = [[ImageDownloader alloc] init];
[downloader downloadImageWithURL:[controller.URList objectForKey:[NSString stringWithFormat:#"%d",downloadIndex]] queue:queue andTag:downloadIndex + 100]; //my custom initialize
downloader.delegate = self;
[queue addOperation:downloader]; //use the addOperation method
Everything works fine in iOS6 but messed up in iOS5 (5.0 on my test device and 5.1 on my SDK), it just doesn't receive any response nor data by performing the methods didReceiveResponse and didReceiveData at all (these 2 methods are not jumped in).
After the timeout was exceeded, the runloop jumps into didFailWithError method and the program stalls.
As I understand, this means the runloop still runs right?
I tried to print out the error and all I got is: The request timed out.
When I reduce the number of downloading instances to 2 then it runs, but not with >=3 downloading instances.
One more information is that my network connection does limit the number of connection. But it work fine in iOS6, why it just doesn't work on iOS5?
I can still load the web in the simulator while the app is downloading.
So what kind of problem is this and how can I get over this problem?
Thanks in advance.
*Update:* as there are many classes and the problem's not been clearly detected yet, I will share here the whole project. You can download it directly from here:
DownloadingImage
As I just found out, if you're using credentials there is a chance that the server will reject them randomly every once in a while. So if you have a check to make sure previousFailureCount == 0 then you will most likely have a bug.
I've just figured out where my problem is, but not really understand why.
In my ImageDownloader class, I set up a runloop with done and currentRunLoop variables.
In the main method, I have a while loop for forcing the currentRunLoop run.
As I remove those "runLoop" stuffs, the app runs smoothly on both iOS6 and iOS5.
So change the entire ImageDownloader.m with these lines then it works (I commented out some useless (say harmful) lines):
//
// ImageLoader.m
// DownloadImagesTableView
//
// Created by Viet Ta Quoc on 6/25/13.
// Copyright (c) 2013 Viet Ta Quoc. All rights reserved.
//
#import "ImageDownloader.h"
#implementation ImageDownloader
#synthesize downloadData,delegate,queue,done,customTag;
NSRunLoop *currentRunLoop;
-(void)downloadImageWithURL:(NSString *)imageUrl queue:(NSOperationQueue*)opQueue andTag:(int)tag{
self.customTag= tag;
self.queue = opQueue;
// self.done = NO;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:imageUrl] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection start];
// currentRunLoop = [NSRunLoop currentRunLoop];
NSLog(#"Start downloading image %d...",customTag);
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(#"Received response...");
downloadData=[[NSMutableData alloc] initWithLength:0];
expectedDataLength=[response expectedContentLength];
NSLog(#"Image %d size: %lld kb",customTag,[response expectedContentLength]/1024);
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
float receivedLenght = [data length];
receivedDataLength=(receivedDataLength+receivedLenght);
float progress=(float)receivedDataLength/(float)expectedDataLength;
[delegate updateProgess:progress andIndex:[NSIndexPath indexPathForRow:customTag-100 inSection:0]];
[self.downloadData appendData:data];
// NSLog(#"Percentage of data received of tag %d: %f %%",self.customTag,progress*100);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
[delegate finishedDownloadingImage:downloadData andTag:customTag];
// done = YES;
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Warning" message:#"Network Connection Failed?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:nil, nil];
// NSLog(#"%#",[error debugDescription]);
NSLog(#"Connection failed! Error - %# %#",[error localizedDescription],[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
[alert show];
}
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
NSLog(#"Got here *(*&(**&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&(*&");
}
-(void)main{
// do{
//// NSLog(#"Running....1");
// [currentRunLoop runUntilDate:[NSDate distantFuture]];
// // [currentRunLoop run];
// } while (!done);
// [currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
#end
Thank you guys for your supports.
==================================================================================
P/s: for anyone who interested in this problem, I update here my entire solution: DownloadImage_Final

How do I read data using CocoaAsyncSocket?

I have created a TCP Socket connection in my appDelegate didFinishLaunchingWithOptions method. That was the easy part, and I have successfully connected to my server. I am having great difficulty with reading the data from the server in my View. I have been looking through tutorials on how to appropriately (step by step) read data using CocoaAsyncSocket, but I haven't come across anything useful.
This is my code from my appDelegate:
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
socket = [[AsyncSocket alloc] initWithDelegate:self];
[self connect];
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewController = [[[tekMatrixViewController alloc] initWithNibName:#"tekMatrixViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
And here is my connect method, at the bottom of the appDelegate file:
- (void)connect
{
[socket connectToHost:#"9.5.3.3" onPort:11005 error:nil];
}
That was the easy part. I now need to read data from the server. I know some kind of NSData or NSMutableData object needs to be created for to take the value of the data I read from the server. I just have been very unsuccessful in finding any tutorial or documentation that points me in the right direction. There are several different read functions, some with different parameters, etc. If anyone could point me to a resource that goes over this in depth*(I am a newbie, after all =P)* I would really appreciate it -- Or if somebody knows of an easy way to accomplish this goal and wouldn't mind providing sample code here :D
This is the library I'm using: CocoaAsyncSocket. I'm using the library AsyncSocket.h and AsyncSocket.m
I've been stuck at this for hours, so any help would be great appreciated.
Thanks!
This should work:
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length])];
NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
if(msg)
{
NSLog(#"RX:%#",msg);
}
}
You should also implement some other delegate methods, for example:
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
NSLog(#"error - disconnecting");
//you'd probably want to start reconnecting procedure here...
}
- (void)onSocketDidDisconnect:(AsyncSocket *)sock
{
NSLog(#"disconnected");
}
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
NSLog(#"connected");
}
EDIT: if memory serves me right there is some documentation and also some examples available with the library.

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.

Is it me or xcode... Somethings wrong with my braces and it just causes errors! Please help! iPhone SDK + Urban Push

Help!
I cant find whats wrong. My code is up and mostly running and i needed to incorporate Urban Air push notification and there is something wrong with my code. If there is a better or different way to incorporate this that works without my errors I would appreciate that.
I took this code from a tut of Urban Airmail. I wasnt sure what to inlude and what not to from the sample app.
Now for my code. Ill notate where I get errors They are stray / errors and expected ; b4 :
errors
If you could fix the code that would be awesome!
//
// SuperSlickAppDelegate.m
// SuperSlick
//
// Created by on 8/2/10.
// Copyright __MyCompanyName__ 2010. All rights reserved.
//
#import <SystemConfiguration/SCNetworkReachability.h>
#include <netinet/in.h>
#import "SuperSlickAppDelegate.h"
#import "SuperSlickViewController.h"
#import "Reachability.h"
#implementation SuperSlickAppDelegate
#synthesize window;
#synthesize viewController;
#pragma mark -
#pragma mark Application lifecycle
#define kApplicationKey #"rnftzaemRp2HJMsNjwZvGQ"
#define kApplicationSecret #"X1XdTjdWQIaL72e-gXew5A"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
Reachability *r = [Reachability reachabilityWithHostName:#"google.com"];
NetworkStatus internetStatus = [r currentReachabilityStatus];
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN))
{
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:#"No Internet Connection" message:#"You require an internet connection via WiFi or cellular network to use this! Try the settings app for WiFi Connectivity." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[myAlert show];
[myAlert release];
}
//Register for notifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert)];
//ERROR HERE in line above Stray 357
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)_deviceToken {
//ERROR HERE Wrong type argument to unary minus + stray
// Get a hex string from the device token with no spaces or < >
self.deviceToken = [[[[_deviceToken description] stringByReplacingOccurrencesOfString:#"<"withString:#""]
stringByReplacingOccurrencesOfString:#">" withString:#""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
NSLog(#"Device Token: %#", self.deviceToken);
if ([application enabledRemoteNotificationTypes] == 0) {
NSLog(#"Notifications are disabled for this application. Not registering with Urban Airship");
return;
}
// this is straight out of the UA sample code
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
NSString *UAServer = #"https://go.urbanairship.com";
NSString *urlString = [NSString stringWithFormat:#"%#%#%#/", UAServer, #"/api/device_tokens/", self.deviceToken];
NSURL *url = [NSURL URLWithString: urlString];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
request.requestMethod = #"PUT";
// Authenticate to the server
request.username = kApplicationKey;
request.password = kApplicationSecret;
[request setDelegate:self];
[request setDidFinishSelector: #selector(registrationSuccessMethod:)]; // if you want to do something with the token
[request setDidFailSelector: #selector(requestWentWrong:)];
[queue addOperation:request];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *) error {
NSLog(#"Failed to register with error: %#", error);
}
- (void)requestWentWrong: (ASIHTTPRequest *)request {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSError *_error = [request error];
NSLog(#"ERROR: NSError query result: %#", _error);
UIAlertView *someError = [[UIAlertView alloc] initWithTitle:
#"Network error" message: NSLocalizedString( #"Error registering with notifiction server",
#"Error registering with notifiction server")
delegate: self
cancelButtonTitle: #"OK"
otherButtonTitles: nil];
[someError show];
[someError release];
}
// Add the view controller's view to the window and display.
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
}
- (void)applicationWillResignActive:(UIApplication *)application {
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application {
/*
Called when the application is about to terminate.
See also applicationDidEnterBackground:.
*/
}
#pragma mark -
#pragma mark Memory management
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
/*
Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
*/
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
#end
New Code that has troubles:
//Register for notifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert)];
;}}
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)_deviceToken {
//ERROR HERE Wrong type argument to unary minus and semi colon b4
Doesn't look like you ever ended your application:didFinishLaunchingWithOptions: method - you should have an end brace after this line:
[[UIApplication sharedApplication] registerForRemoteNotificationTypes...
So it should look like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert)];
//ERROR HERE in line above Stray 357
}
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)_deviceToken {
...
}
That should do it.
For your second code block - without seeing the surrounding code it's difficult to see what you're trying to do...you likely shouldn't have:
;}}
It should probably just be:
}
If you are looking for unbalanced parentheses, then this is probably the part that is giving you troubles:
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
}

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