views:

3240

answers:

9

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?

+2  A: 

Did you check out the BSD Sockets in Cocoa-Touch's networking guide?

loarabia
+6  A: 

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.

Mike Abdullah
+8  A: 

http://code.google.com/p/cocoaasyncsocket/

This is what you want.

Genericrich
A: 

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/

m4rkk
+6  A: 

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
Michael Fey
A: 

As pointed out by Genericrich, the AsyncSocket class is just wonderful for dealing with sockets. http://code.google.com/p/cocoaasyncsocket/

Sam V
A: 

An Awesome tutorial. Thanks Michael, Appreciate your efforts.

Arun Thakkar
This is not an answer. Post a comment instead.
Shaggy Frog
A: 

Michael Fey is it possible to post the implementation code for SocketCommunicationManager above??

peptalk
A: 

Implementation code needed for socket communication manager???

peptalk