macosbonjourdistributed-objectsnsconnectionnssocketport

NSSocketPortNameServer portForName:host: slow on Mavericks


With Mavericks [NSSocketPortNameServer portForName:host:] now takes around 5 seconds to resolve localhost. It used to be much much faster, around 0.01 seconds.

My code is the same as in Apple's Introduction to Distributed Objects.

I used to be able to start a child process and connect to it, in less than 0.1 seconds. My file manager runs several child processes and is currently defunct on Mavericks because of this. The app is not sandboxed.

I don't understand why [NSSocketPortNameServer portForName:host:] takes so long. Maybe I'm doing something wrong.

Any advice much appreciated?


Server code

This takes around 0.1 second to run.

NSSocketPort* port = [[NSSocketPort alloc] init];
NSConnection* connection = [NSConnection connectionWithReceivePort:port sendPort:nil];
[[NSSocketPortNameServer sharedInstance] registerPort:port name:@"doug"];

Client code that connects to the server

This takes 5 seconds on Mavericks.

This used to take around 0.1 seconds on Mountain Lion and Lion.

NSPort* port = [[NSSocketPortNameServer sharedInstance] portForName:@"doug" host:@"*"];
NSConnection* connection = [NSConnection connectionWithReceivePort:nil sendPort:port];

I have also tried with nil, like this [[NSSocketPortNameServer sharedInstance] portForName:name host:nil]. It made no difference.

If I invalidate the connection and tries to connect again then [[NSSocketPortNameServer sharedInstance] portForName:name host:nil] also takes 5 seconds.


What could be causing this

When I dump the DNS configuration with scutil --dns, I see that the local domain has a 5 second timeout. I suspect that this timeout was set to 0 seconds before Mavericks. I can't ask all users to reset this timeout, so I will continue investigating what to do on Mavericks to avoid this timeout.


Solution

  • Finally solved it.

    I ended up abandoning -portForName:host: and instead pass the port number via commandline to the child process.

    The child process contacts the parent process this way:

    int port = /* parent process' port number passed via command line */
    NSSocketPort *port = [[NSSocketPort alloc] initRemoteWithTCPPort:port host:nil];
    NSConnection* connection = [NSConnection connectionWithReceivePort:nil sendPort:port];
    

    The child process gets the port number from the parent process via commandline. The parent process find its own port number via this category.

    @implementation NSSocketPort (ObtainPortNumber)
    
    -(int)portNumber {
            NSSocketNativeHandle sock = [self socket];
            NSData *address = self.address;
            if ([address length] != sizeof(struct sockaddr_in)) {
                    NSLog(@"NSSocketPort (ObtainPortNumber) - Mismatch size of address vs size of sockaddr_in.");
                    return -1;
            }
    
            struct sockaddr_in addr = *((struct sockaddr_in*)[address bytes]);
    
            socklen_t len = sizeof(addr);
            if (getsockname(sock, (struct sockaddr *)&addr, &len) == -1) {
                    NSLog(@"NSSocketPort (ObtainPortNumber) - getsockname failed.");
                    return -1;
            }
    
            int portNumber = ntohs(addr.sin_port);
            return portNumber;
    }
    
    @end