Question

I've got a simple client & server application semi working with CocoaAsyncSocket. I can use the client to connect to the server, and can pass data back and forth between them and the delegate methods seem to fire correctly. My problem is that I can only send data back to a (AsyncSocket *) object that is passed to one of delegate methods. When accepting a connection I add the socket to an NSMutableArray, but later if I try to use the socket writing fails. So

[connectedSockets addObject:newSocket];
//then later
[[connectedSockets objectAtIndex:i] writeData:someData withTimeout:-1 tag:0];

doesn't work. Code is below for server and client. The initial communication does work, but when my notification gets called no data is sent, even though the NSLog statement tests the socket in the array and gets the proper IP Address & port #:

Server:

- (id)init
{
    self = [super init];
    if (self) {
        listenSocket = [[AsyncSocket alloc] initWithDelegate:self];
        [listenSocket setRunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
        [listenSocket acceptOnPort:42381 error:nil];


        connectedSockets = [[NSMutableArray alloc] init];
        newNotificationDictArray = [[NSMutableArray alloc] init];
        updateNotificationDictArray = [[NSMutableArray alloc] init];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(newJobNotification:)
                                                     name:@"NewJob"
                                                   object:nil];
    }

    return self;

}

-(void)onSocketDidDisconnect:(AsyncSocket *)sock
{
    [connectedSockets removeObject:sock];
}

- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
    [connectedSockets removeObject:sock];
}

- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
    [connectedSockets addObject:newSocket];
}


- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
if ([newNotificationDictArray count] > 0) {
    NSMutableData *data = [[NSMutableData alloc] init];
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    [archiver encodeObject:newNotificationDictArray forKey:@"DictArray"];
    [archiver finishEncoding];
    [archiver release];
    [sock writeData:data withTimeout:-1 tag:0];
    [data release];
}

[sock readDataWithTimeout:-1 tag:0];
}

- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    [sock readDataWithTimeout:-1 tag:0];
}

- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
[sock readDataWithTimeout:-1 tag:0];
}

-(void) newJobNotification: (NSNotification *) notification
{
NSDictionary *dict = [notification userInfo];
[newNotificationDictArray addObject:dict];
[updateNotificationDictArray addObject:dict];
NSMutableData *updateData = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:updateData];
[archiver encodeObject:updateNotificationDictArray forKey:@"DictArray"];
[archiver finishEncoding]
[updateNotificationDictArray removeAllObjects];
[archiver release];
NSLog(@"Attempting to write to %@:%i", [[connectedSockets objectAtIndex:0] connectedHost], [[connectedSockets objectAtIndex:0] connectedPort]) 
[[connectedSockets objectAtIndex:0] writeData:updateData withTimeout:-1 tag:0];
}

and then the client:

- (id)init
{
self = [super init];
if (self) {
    socket = [[AsyncSocket alloc] initWithDelegate:self];
}
return self;
}

-(void) connectToHost: (NSString *) address
{

}

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock 
{
[sock retain];
[socket readDataWithTimeout:-1 tag:0];
return YES;
}

-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port 
{    
NSLog(@"Did Connect on port %i", [sock connectedPort]);
}

-(void) disconnectFromRemoteHost
{
[socket disconnect];
}

-(void)onSocketDidDisconnect:(AsyncSocket *)sock
{
NSLog(@"Did Disconnect");
}

- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
NSLog(@"Disconnect Error: %@", [err localizedDescription]);
}

- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
[socket readDataWithTimeout:-1 tag:0];
}

- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    NSArray *dictArray = [[unarchiver decodeObjectForKey:@"DictArray"] retain];
    [unarchiver finishDecoding];
    [unarchiver release];
    for (NSDictionary *dict in dictArray) {
        if ([[dict objectForKey:@"Kind"] isEqualTo:@"New"])
            [[NSNotificationCenter defaultCenter] postNotificationName:@"NewJob" object:self userInfo:dict];
        else if ([[dict objectForKey:@"Kind"] isEqualTo:@"Update"])
            [[NSNotificationCenter defaultCenter] postNotificationName:@"JobUpdate" object:self userInfo:dict];
    }
[socket readDataWithTimeout:-1 tag:0];
}

Help me stackoverflow, you're my only hope!

Was it helpful?

Solution

I see 2 potential problems with your posted code. The first seems unlikely to be causing your problems, but I'll post it anyway. In your client code you have:

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock 
{
    [sock retain];
    [socket readDataWithTimeout:-1 tag:0];
    return YES;
}

I'm not sure why you would be retaining "sock" here. You already have a retained copy of the socket you created and this seems likely to result in a leak.

As for the second problem, which is more likely to be the cause of your failures, here is what I think is happening. Looking at the documentation for AsyncSocket I see this:

However, AsyncSocket is not thread-safe. You must only invoke methods on AsyncSocket from the thread/runloop on which the socket is configured. If you find yourself on a thread which is different from the thread on which AsyncSocket is running, and you need to invoke a method on AsyncSocket, then you must use a method like performSelector:onThread: to ensure the method is invoked on AsyncSocket's thread (and thereby maintaining thread-safety).

Your code that receives notifications and triggers a "later" write is likely in another thread, so even through the thread is valid the write fails to do what you expect. The AsyncSocket documentation goes on to suggest you use GCDAsyncSocket if you want thread safety.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top