Question

I have the excellent GCDAsyncSocket running perfectly on an iOS app I have developed.

I was just playing around with setting up a Mac OSX command line program that uses the library in a similar way to log to SQLite DB but can't get it to even attempt to connect to host. No errors get generated. The program doesn't crash or anything so don't have an idea about why it's not working. Does anyone have an idea why this won't work?

The console only prints out the following (with no connect/disconnect/read/write logging i.e. the socket delegate methods are not being called):

Attempting to connect to host: 192.168.1.2 on port: 1234 to refresh

Here is quite a bit of the code I am using:

main.m

#import <Foundation/Foundation.h>
#import "LoggerClass.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LoggerClass *logger = [[LoggerClass alloc] init];
        [logger startLogging];
        while (logger.status == 0) {
            sleep(1);
            continue;
        }
        return 0;
    }
}

LoggerClass.h

#import <Foundation/Foundation.h>
#import "Device.h"

@interface LoggerClass : NSObject <DeviceProtocol>

@property (nonatomic, strong) FMDatabase *database;
@property (nonatomic, strong) NSArray *devices;
@property (nonatomic) int status;
- (void)startLogging;

@end

LoggerClass.m

#import "LoggerClass.h"
#import "FMDatabase.h"

#define kLoggingInProgress 0
#define kLoggingCompleted 1

@implementation LoggerClass

@synthesize database = _database;
@synthesize devices = _devices;
@synthesize status = _status;

- (id)init {
    if (self = [super init]) {
        self.status = kLoggingInProgress;

        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *docsPath = [paths objectAtIndex:0];
        NSString *path = [docsPath stringByAppendingPathComponent:@"database.sqlite"];  
        self.database = [FMDatabase databaseWithPath:path];

        Device *d1 = [Device deviceWithName:@"Device 1" address:@"192.168.1.2" delegate:self];      
        self.devices = [NSArray arrayWithObjects:d1, nil];
    }   
    return self;
}

- (void)startLogging {
    for (Device *d in self.devices) {
        [d refresh];
    }
}

- (void)didUpdateDevice:(Device *)device {

    // Insert DB entry code

    NSLog(@"%@ has finished Logging", device.name);
    self.status = kLoggingCompleted; // This would obviously register completed if only 1 device returned but for sake of this test that fine
}

@end

Device.h

#import <Foundation/Foundation.h>
#import "GCDAsyncSocket.h"

@protocol DeviceProtocol;

@interface Device : NSObject

@property (nonatomic, weak) id<DeviceProtocol> delegate;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *address;
@property (nonatomic, strong) GCDAsyncSocket *socket;

+ (Device *)deviceWithName:(NSString *)n address:(NSString *)a delegate:(id<DeviceProtocol>)d;
- (void)refresh;

@end

@protocol DeviceProtocol <NSObject>
@required
- (void)didUpdateDevice:(Device *)device;
@end

Device.m

#import "Device.h"

#define DEVICE_PORT 1234

@implementation Device

@synthesize delegate = _delegate;
@synthesize name = _name;
@synthesize address = _address;
@synthesize socket = _socket;

- (id)initWithName:(NSString *)name andAddress:(NSString *)address andDelegate:(id<DeviceProtocol>)delegate { // Designated Initialiser
    if (self = [super init]) {

        self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

        self.name = name;
        self.address = address;
        self.delegate = delegate;
    }
    return self;
}

+ (Device *)deviceWithName:(NSString *)n address:(NSString *)a delegate:(id<DeviceProtocol>)d {
    return [[Device alloc] initWithName:n andAddress:a andDelegate:d];
}

#pragma mark - GCD Async Socket Delegate Methods

- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port {
    NSLog(@"Connected to: %@", self.address);
}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)error {
    NSLog(@"Socket for %@ disconnected %@.", self.address, error);

    if (self.delegate) [self.delegate didUpdateDevice:self];
}

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    NSLog(@"socket:didWriteDataWithTag:");
}

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    NSLog(@"socket:didReadData:withTag:");

    [self.socket disconnect];   
}

- (void)refresh {
    if ([self.address length] == 0) { [self.delegate didUpdateDevice:self]; return; }

    NSLog(@"Attempting to connect to host: %@ on port: %i to refresh", self.address, DEVICE_PORT);
    NSError *error = nil;
    if (![self.socket connectToHost:self.address onPort:DEVICE_PORT withTimeout:15 error:&error]) NSLog(@"ERROR: %@", error);

    NSData *dataToSend;
    // Build byte data here to send to device (exact same data works on iOS)

    [self.socket writeData:dataToSend withTimeout:10 tag:0];
    [self.socket readDataWithTimeout:-1 tag:0];
}

@end
Was it helpful?

Solution

I just re-read your comment and realized what your main() looks like. I think that's where your problem lies. The callbacks are probably sitting in the main dispatch queue but it never gets a chance to execute them. In a Cocoa application, the main queue normally runs as part of the main run loop, but you aren't starting a run loop.

See the dispatch_get_main_queue() documentation.

I think the simplest initial fix is to replace your spin loop with this:

dispatch_main();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top