连接后立即与服务器断开

4
我使用GCDAsyncsocket库编写了一个Singleton类,以使用Bonjour与任何具有相同服务的其他设备建立连接。
在一个设备上,我使用其方法“startPublishing”将其作为主机(服务器),从另一个设备(客户端)的应用程序中调用“StartBrowsing”以查找网络中可用的设备。当用户在该网络中选择任何服务时,我调用“initConnectionWithService”方法,通过解析NetService的地址来启动连接流程。
“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

然而,在执行以上操作时,非常意外的事情发生了,充当客户端的设备在didConnectedToHost中获得回调,然后立即另一个回调出现在didDisconnected中。

客户端设备日志

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}

服务器设备日志

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)

1
你是如何在委托中存储套接字的引用的?你是使用强引用属性还是实例变量? - Paulw11
是的,所有套接字的引用都是强引用。这个类是单例模式,所以我认为在这种情况下特别是引用方面不会有问题。 - Mrug
该类是单例模式,但当您获取新连接时返回的套接字需要在某处保存。单例本身不会持有对已连接套接字的引用。 - Paulw11
但是你的评论给了我一个提示,我需要在主机端将已接受的套接字存储到某个地方,我已经做到了,现在它可以工作了。谢谢Paulw11。 - Mrug
1个回答

4

Paulw11的评论帮助我找到了解决方案。实际上,我在客户端存储了Socket,但忘记了在回调方法“didAcceptNewSocket”中存储新Socket的引用。

因此,didAcceptNewSocket方法应该如下所示:

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

因此,在这种方法中接收到的新套接字可以持续进行进一步的通信。在之前的情况下,它会在方法结束时被释放。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接