Domanda

Sto facendo un'app con due componenti, un componente per iPhone e un componente Mac.Dovrebbero comunicare tra loro via Bonjour.Io uso il seguente codice sull'estremità MAC per trovare una porta per il servizio:

NSSocketPort *socket = [[NSSocketPort alloc] init];
struct sockaddr *addr = (struct sockaddr *)[[socket address] bytes];
int port = 9876;
if(addr->sa_family == AF_INET) {
    port = ntohs(((struct sockaddr_in *)addr)->sin_port);
} else if(addr->sa_family == AF_INET6) {
    port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
} else {
    [socket release];
    socket = nil;
    NSLog(@"The family is neither IPv4 nor IPv6. Can't handle!!!");
}
.

Posso anche utilizzare questo codice su Iphone End ed eseguire l'app nel simulatore e la connessione funziona bene.Tuttavia, quando ho provato a eseguire questo codice su un iPhone reale, ho scoperto che NSSocketport non è disponibile su iPhone.Quando provo ad avviare il servizio con la porta 9876, Mac APP visualizza un errore rifiuto di connessione quando provo a connetterti con esso.Quindi, come posso trovare una porta da usare senza usare nssocketport?

È stato utile?

Soluzione

OK Ho guardato il codice Witp di Apple e la modifica leggermente per scrivermi un metodo per ottenere una porta:

- (int) getPort {
CFSocketContext socketCtxt = {0, self, NULL, NULL, NULL};   

// Start by trying to do everything with IPv6.  This will work for both IPv4 and IPv6 clients 
// via the miracle of mapped IPv4 addresses.    

CFSocketRef witap_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET6, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, nil, &socketCtxt);
uint32_t protocolFamily;

if (witap_socket != NULL)   // the socket was created successfully
{
    protocolFamily = PF_INET6;
} else // there was an error creating the IPv6 socket - could be running under iOS 3.x
{
    witap_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, nil, &socketCtxt);
    if (witap_socket != NULL)
    {
        protocolFamily = PF_INET;
    }
}

/*if (NULL == witap_socket) {
    if (error) *error = [[NSError alloc] initWithDomain:TCPServerErrorDomain code:kTCPServerNoSocketsAvailable userInfo:nil];
    if (witap_socket) CFRelease(witap_socket);
    witap_socket = NULL;
    return NO;
}*/


int yes = 1;
setsockopt(CFSocketGetNative(witap_socket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));

// set up the IP endpoint; use port 0, so the kernel will choose an arbitrary port for us, which will be advertised using Bonjour
if (protocolFamily == PF_INET6)
{
    struct sockaddr_in6 addr6;
    memset(&addr6, 0, sizeof(addr6));
    addr6.sin6_len = sizeof(addr6);
    addr6.sin6_family = AF_INET6;
    addr6.sin6_port = 0;
    addr6.sin6_flowinfo = 0;
    addr6.sin6_addr = in6addr_any;
    NSData *address6 = [NSData dataWithBytes:&addr6 length:sizeof(addr6)];

    CFSocketSetAddress(witap_socket, (CFDataRef)address6);
    /*if (kCFSocketSuccess != CFSocketSetAddress(witap_socket, (CFDataRef)address6)) {
        if (error) *error = [[NSError alloc] initWithDomain:TCPServerErrorDomain code:kTCPServerCouldNotBindToIPv6Address userInfo:nil];
        if (witap_socket) CFRelease(witap_socket);
        witap_socket = NULL;
        return NO;
    }*/

    // now that the binding was successful, we get the port number 
    // -- we will need it for the NSNetService
    NSData *addr = [(NSData *)CFSocketCopyAddress(witap_socket) autorelease];
    memcpy(&addr6, [addr bytes], [addr length]);
    return ntohs(addr6.sin6_port);

} else {
    struct sockaddr_in addr4;
    memset(&addr4, 0, sizeof(addr4));
    addr4.sin_len = sizeof(addr4);
    addr4.sin_family = AF_INET;
    addr4.sin_port = 0;
    addr4.sin_addr.s_addr = htonl(INADDR_ANY);
    NSData *address4 = [NSData dataWithBytes:&addr4 length:sizeof(addr4)];

    CFSocketSetAddress(witap_socket, (CFDataRef)address4);
    /*if (kCFSocketSuccess != CFSocketSetAddress(witap_socket, (CFDataRef)address4)) {
        if (error) *error = [[NSError alloc] initWithDomain:TCPServerErrorDomain code:kTCPServerCouldNotBindToIPv4Address userInfo:nil];
        if (witap_socket) CFRelease(witap_socket);
        witap_socket = NULL;
        return NO;
    }*/

    // now that the binding was successful, we get the port number 
    // -- we will need it for the NSNetService
    NSData *addr = [(NSData *)CFSocketCopyAddress(witap_socket) autorelease];
    memcpy(&addr4, [addr bytes], [addr length]);
    return ntohs(addr4.sin_port);
}

}
.

Ho tirato fuori un sacco di cose di errore, quindi probabilmente non è ottimale, ma funziona.

Altri suggerimenti

A partire da iOS 7, Nsnetservice può creare e legare la presa per te.Se inizializzato con Port Zero, sceglierà una porta casuale:

NSNetService *service = [[NSNetService alloc] initWithDomain:@"local." type:@"_test._tcp." name:@"Test Service" port:0];
[service publishWithOptions:NSNetServiceListenForConnections];
.

Il servizio invierà netService:didAcceptConnectionWithInputStream:outputStream: al suo delegato quando viene stabilita una connessione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top