Disconnecting with server immediately after connecting
Asked Answered
T

1

4

I have written a Singleton Class using GCDAsyncsocket Library to establish connection with any other device having same service using Bonjour.

On one device I am using its Method "startPublishing" to make it a Host(Server), from the Application on another Device(Client) I am calling "StartBrowsing" to find out the available Devices in Network. When user selects any of service in that Network I am calling method "initConnectionWithService", that initiate Connection flow by resolving address of NetService to connect.

BonjourUtilClass.h

@interface BonjourUtilClass : NSObject<GCDAsyncSocketDelegate,NSNetServiceDelegate,NSNetServiceBrowserDelegate>{
    NSNetService *netServiceToPublish;
    GCDAsyncSocket *socketPub;

    NSNetServiceBrowser *netServiceToBrowse;
    GCDAsyncSocket *socketSub;
    NSMutableArray *mutArrServices;


    GCDAsyncSocket *socketConnected;

}

+(id)sharedInstance;

-(void)startPublishing;
-(void)startBrowsing;
-(void)initConnectionWithService:(NSNetService*)netServiceToConnect;
-(void)disconnectWithCurrent;    

@end

BonjourUtilClass.m

static BonjourUtilClass *sharedObject = nil;
@implementation BonjourUtilClass

+(id)sharedInstance{
    if(!sharedObject){
        sharedObject = [[BonjourUtilClass alloc]init];
    }
    return sharedObject;
}


#pragma mark - Browsing
-(void)startBrowsing{

    if(mutArrServices){
        [mutArrServices removeAllObjects];
    }else{
        mutArrServices = [NSMutableArray array];
    }

    netServiceToBrowse = [[NSNetServiceBrowser alloc]init];
    netServiceToBrowse.delegate= self;
    [netServiceToBrowse searchForServicesOfType:@"_mrug._tcp" inDomain:@"local."];

}
-(void)stopBrowsing{

}
-(void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing{
    [mutArrServices addObject:aNetService];

    if(!moreComing) {
        // Sort Services
        [mutArrServices sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]];

        // Update Table View
        [[NSNotificationCenter defaultCenter]postNotificationName:kNotifyReloadList object:mutArrServices];
    }
}
-(void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing{
    [mutArrServices removeObject:aNetService];

    if(!moreComing) {
        // Sort Services
        [mutArrServices sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]];

        // Update Table View
        [[NSNotificationCenter defaultCenter]postNotificationName:kNotifyReloadList object:mutArrServices];
    }

}
-(void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)aNetServiceBrowser{
    NSLog(@"Search browser Did STOP search..");
    [self stopBrowsing];
}
-(void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didNotSearch:(NSDictionary *)errorDict{
    NSLog(@"Search browser Did not search..");
    [self stopBrowsing];
}


#pragma mark - NetService Delegate
-(void)startPublishing{

    socketPub = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    NSError *aError;
    if([socketPub acceptOnPort:0 error:&aError]){
        netServiceToPublish = [[NSNetService alloc]initWithDomain:@"local." type:@"_mrug._tcp" name:@"" port:socketPub.localPort];
        netServiceToPublish.delegate =self;
        [netServiceToPublish publish];

    }else{
        NSLog(@"Unable To Create Socket..");
    }
}

//NetService Delegates
-(void)netService:(NSNetService *)sender didNotPublish:(NSDictionary *)errorDict{
    NSLog(@"Failed To Publish : Domain=%@ type=%@ name=%@ info=%@",sender.domain,sender.type,sender.name,errorDict);
}
-(void)netServiceDidPublish:(NSNetService *)sender{
    NSLog(@"Service Published : Domain=%@ type=%@ name=%@ port=%li",sender.domain,sender.type,sender.name,(long)sender.port);
}

//Resolving Address
- (void)netService:(NSNetService *)service didNotResolve:(NSDictionary *)errorDict {
    [service setDelegate:nil];
}

- (void)netServiceDidResolveAddress:(NSNetService *)service {
    // Connect With Service
    if ([self connectWithService:service]){
        NSLog(@"Did Connect with Service: domain(%@) type(%@) name(%@) port(%i)", [service domain], [service type], [service name], (int)[service port]);
    } else {
        NSLog(@"Unable to Connect with Service: domain(%@) type(%@) name(%@) port(%i)", [service domain], [service type], [service name], (int)[service port]);
    }
}


#pragma mark - GCDSocket delegates

-(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{
    NSLog(@"Accepted new Socket: HOST : %@ , CONNECTION PORT :%li",newSocket.connectedHost,(long)newSocket.connectedPort);
}
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
    NSLog(@"Socket DisConnected %s,%@,%@",__PRETTY_FUNCTION__, sock,err);
    if(socketPub == sock){
        socketPub.delegate = nil;
        socketPub = nil;
    }else if (socketConnected == sock){
        socketConnected.delegate=nil;
        socketConnected = nil;
    }
}
- (void)socket:(GCDAsyncSocket *)socket didConnectToHost:(NSString *)host port:(UInt16)port {
    NSLog(@"Socket Did Connect to Host: %@ Port: %hu", host, port);

    // Start Reading
    [socket readDataToLength:sizeof(uint64_t) withTimeout:-1.0 tag:0];
}

#pragma mark - Connection Methods
-(void)disconnectWithCurrent{
    if(socketConnected){
        [socketConnected disconnect];
        socketConnected.delegate = nil;
        socketConnected = nil;
    }
}

-(void)initConnectionWithService:(NSNetService*)netServiceToConnect{
    // Resolve Service
    [netServiceToConnect setDelegate:self];
    [netServiceToConnect resolveWithTimeout:30.0];
}



- (BOOL)connectWithService:(NSNetService *)service {
    BOOL _isConnected = NO;

    // Copy Service Addresses
    NSArray *addresses = [[service addresses] mutableCopy];
    if (!socketConnected || ![socketConnected isConnected]) {
        // Initialize Socket
        socketConnected = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

        // Connect
        while (!_isConnected && [addresses count]) {
            NSData *address = [addresses objectAtIndex:0];
            NSError *error = nil;
            if ([socketConnected connectToAddress:address error:&error]) {
                _isConnected = YES;

            } else if (error) {
                NSLog(@"Unable to connect to address. Error %@ with user info %@.", error, [error userInfo]);
            }
        }

    } else {
        _isConnected = [socketConnected isConnected];
    }
    return _isConnected;
}
@end

But, On execution of above things very unexpected things happending, Device which is acting as Client is getting callback in didConnectedToHost and immediatly, another Callback is coming that is in didDisconnected

Logs on Client Device

2014-12-11 15:16:32.512 GCDSocketDemo[1419:71238] Did Connect with Service: domain(local.) type(_mrug._tcp.) name(ind506Bonjour) port(52026)
2014-12-11 15:16:32.659 GCDSocketDemo[1419:71238] Socket Did Connect to Host: 10.2.4.130 Port: 52026
2014-12-11 15:16:32.660 GCDSocketDemo[1419:71238] -[AppDelegate socketDidDisconnect:withError:],<GCDAsyncSocket: 0x7fa0a3533b90>,Error Domain=GCDAsyncSocketErrorDomain Code=7 "Socket closed by remote peer" UserInfo=0x7fa0a3532f70 {NSLocalizedDescription=Socket closed by remote peer}

Logs On Server Device

2014-12-11 15:15:48.546 GCDSockrtMacDemo[1397:70851] Service Published : Domain=local. type=_mrug._tcp. name=ind506Bonjour port=52026
2014-12-11 15:16:32.585 GCDSockrtMacDemo[1397:70851] Accepted new Socket: HOST : 10.2.4.130 , CONNECTION PORT :52029
2014-12-11 15:16:32.613 GCDSockrtMacDemo[1397:70851] -[BonjourUtilClass socketDidDisconnect:withError:],(null),(null)
Theone answered 11/12, 2014 at 9:43 Comment(4)
How are you storing the reference to the socket in your delegate? Are you using a strong property or an iVar?Scarab
Yes all references of Sockets are Strong. This Class is singleton one, so I think there can not be issue of references particularly in this case.Theone
The class is a singleton but the sockets that are returned when you get a new connection needs to be held somewhere. The singleton itself won't hold a reference to the connected socketScarab
But your comment gave me a Hint, I need to store Accepted Socket to somewhere on Host side, I done that and Its working. Thanks Paulw11.Theone
T
4

Comment of Paulw11 Helped me to find out the Solution. Actually I stored Socket on client side, but forgot to Store reference of new Socket getting in callback method "didAcceptNewSocket".

So the Method didAcceptNewSocket should be as below:

-(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{
    NSLog(@"Accepted new Socket: HOST : %@ , CONNECTION PORT :%li",newSocket.connectedHost,(long)newSocket.connectedPort);
    socketConnected = newSocket;
}

So that newSocket received in this method can be persist for further communication. In earlier case it was releasing at end of method.

Theone answered 11/12, 2014 at 10:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.