How do I read data using CocoaAsyncSocket? - iphone

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.

Related

Application Freezing when calling CocoaAsyncMethod scheduleDequeueRead

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
:)

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

Cocoa-Touch framework for speaking to a TCP socket? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 6 years ago.
Improve this question
I have a daemon running on a server that's latched onto a TCP/IP port. I'm looking to see if there's currently any support iPhone/Cocoa-touch frameworks that gives a nice OO wrapper for speaking to the daemon over an IP socket. I need to be able to interactively query the daemon with commands and retrieve back information.
If there isn't any OO wrappers for such a task, what's the next best bet?
http://code.google.com/p/cocoaasyncsocket/
This is what you want.
Here is some sample code from the previously mentioned AsyncSocket code that I modified into a class called SocketCommunicationManager.
A few things to note:
Our messages are being delimited with newline characters (\n) so when reading data from the socket I had to make sure to use the right constant from the AsyncSocket class (LFData in our case). AsyncSocket also provides CRLFData, CRData, and ZeroData as predefined message delimiters.
I set up the SocketCommunicationManager to always wait for an incoming message after I received and acted on a previous one. To accomplish that I used the (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag method. This method will wait until data is written to the socket, read up until the specified delimiter, and then call the delegate method (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
The SocketCommunicationManager uses NSNotificationCenter to publish any messages received from the socket. These messages are named kNotification and the message is put into the userInfo dictionary using the kNotificationMessage key.
Everything read from the socket is wrapped in an NSData object, so you'll have to decode that data after it is received.
Here's the code:
#import <Foundation/Foundation.h>
extern NSString * const kNotification;
extern NSString * const kNotificationMessage;
#class AsyncSocket;
#interface SocketCommunicationManager : NSObject {
AsyncSocket *socket;
BOOL isRunning;
NSNotificationCenter* notificationCenter;
}
#property (readwrite, assign) BOOL isRunning;
- (void)connectToHost:(NSString *)hostName onPort:(int)port;
- (void)sendMessage:(NSString *)message;
- (void)disconnect;
#end
#import "SocketCommunicationManager.h"
#import "AsyncSocket.h"
NSString * const kNotification = #"kNotification";
NSString * const kNotificationMessage = #"kNotificationMessage";
#implementation SocketCommunicationManager
#synthesize isRunning;
- (id) init {
if (!(self = [super init]))
return nil;
socket = [[AsyncSocket alloc] initWithDelegate:self];
[self setIsRunning:NO];
notificationCenter = [NSNotificationCenter defaultCenter];
return self;
}
- (void)connectToHost:(NSString *)hostName onPort:(int)port {
if (![self isRunning]) {
if (port < 0 || port > 65535)
port = 0;
NSError *error = nil;
if (![socket connectToHost:hostName onPort:port error:&error]) {
NSLog(#"Error connecting to server: %#", error);
return;
}
[self setIsRunning:YES];
} else {
[socket disconnect];
[self setIsRunning:false];
}
}
- (void)disconnect {
[socket disconnect];
}
- (void)dealloc {
[super dealloc];
[socket disconnect];
[socket dealloc];
}
- (void)sendMessage:(NSString *)message {
NSString *terminatedMessage = [message stringByAppendingString:#"\r\n"];
NSData *terminatedMessageData = [terminatedMessage dataUsingEncoding:NSASCIIStringEncoding];
[socket writeData:terminatedMessageData withTimeout:-1 tag:0];
}
#pragma mark AsyncSocket Delegate
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port {
NSLog(#"Connected to server %#:%hu", host, port);
[sock readDataToData:[AsyncSocket LFData] withTimeout:-1 tag:0];
}
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSData *truncatedData = [data subdataWithRange:NSMakeRange(0, [data length] - 1)];
NSString *message = [[[NSString alloc] initWithData:truncatedData encoding:NSASCIIStringEncoding] autorelease];
if (message)
NSLog(#"%#", message);
else
NSLog(#"Error converting received data into UTF-8 String");
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:message forKey:kNotificationMessage];
[notificationCenter postNotificationName:kNotification object:self userInfo:userInfo];
[sock readDataToData:[AsyncSocket LFData] withTimeout:-1 tag:0];
}
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag {
[sock readDataToData:[AsyncSocket LFData] withTimeout:-1 tag:0];
}
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err {
NSLog(#"Client Disconnected: %#:%hu", [sock connectedHost], [sock connectedPort]);
}
#end
Roughly speaking, going up the stack you have:
BSD sockets
CFSocket
CFReadStream/CFWriteStream/NSInputStream/NSOutputStream
CFHTTPStream
NSURLConnection
Sounds like you want CFSocket, or possibly CFStream.
Did you check out the BSD Sockets in Cocoa-Touch's networking guide?
As Genericrich points out, the Cocoa Async Socket framework is the way to go. This has been around for a while and seen a good deal of use. http://code.google.com/p/cocoaasyncsocket/
As pointed out by Genericrich, the AsyncSocket class is just wonderful for dealing with sockets.
http://code.google.com/p/cocoaasyncsocket/