Question

Je crée une application avec deux composants, un composant iPhone et un composant Mac.Ils sont censés communiquer entre eux via bonjour.J'utilise le code suivant côté Mac pour trouver un port pour le service :

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!!!");
}

Je peux également utiliser ce code du côté de l'iPhone et exécuter l'application dans le simulateur, et la connexion fonctionne correctement.Cependant, lorsque j'ai essayé d'exécuter ce code sur un iPhone réel, j'ai découvert que NSSocketPort n'était pas disponible sur l'iPhone.Lorsque j'essaie de démarrer le service avec le port 9876, l'application Mac affiche une erreur de connexion refusée lorsque j'essaie de m'y connecter.Alors, comment puis-je trouver un port à utiliser sans utiliser NSSocketPort ?

Était-ce utile?

La solution

OK, j'ai regardé le code WITAP d'Apple et je l'ai légèrement modifié pour écrire moi-même une méthode pour obtenir un port:

- (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);
}

}

J'ai sorti beaucoup de choses d'erreur afin que ce ne soit probablement pas optimal, mais cela fonctionne.

Autres conseils

Depuis iOS 7, NSNetService peut créer et lier le socket pour vous.S'il est initialisé avec le port zéro, il choisira un port aléatoire :

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

Le service enverra netService:didAcceptConnectionWithInputStream:outputStream: à son délégué lorsqu'une connexion est établie.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top