iPhone: Bonjour NSNetService IP-адрес и порт - PullRequest
16 голосов
/ 02 июня 2009

Извините, пожалуйста, мой статус новичка в iPhone / Objective-C!

Я обнаружил, что мой HTTP-сервер использует NSNetServiceBrowser, но теперь мне просто нужен IP-адрес и порт найденной службы.

У меня есть что-то вроде следующего в моем методе делегата:

NSNetService* server = [serverBrowser.servers objectAtIndex:0];

NSString            *name = nil;
NSData              *address = nil;
struct sockaddr_in  *socketAddress = nil;
NSString            *ipString = nil;
int                 port;
uint                 i;
for (i = 0; i < [[server addresses] count]; i++)
{
    name = [server name];
    address = [[server addresses] objectAtIndex:i];
    socketAddress = (struct sockaddr_in *)
    [address bytes];
    ipString = [NSString stringWithFormat: @"%s",
                inet_ntoa (socketAddress->sin_addr)];
    port = socketAddress->sin_port;
    NSLog(@"Server found is %s %d",ipString,port);
}

но цикл for никогда не вводится, даже если вызывается делегат. Есть идеи? Спасибо!

Ответы [ 3 ]

41 голосов
/ 12 февраля 2011

Я понимаю, что это старая ветка, но я тоже наткнулся на это. Есть несколько проблем с кодом выше:

  1. Это не подкованный IPv6. На минимум, он должен обнаружить и откажитесь от IPv6-адресов, если остальные вашего приложения может обрабатывать только v4 адреса, но в идеале вы должны быть готов передать оба адреса семьи вверх по течению.

  2. Назначение порта будет генерировать неверные значения для Intel процессоры. Вам нужно использовать htons чтобы исправить это.

  3. Как отметил Эндрю выше, итерация должна использовать расширенный для цикла.

  4. (РЕДАКТИРОВАТЬ: Добавлено это) Как отмечалось в другой связанной ветке, использование inet_ntoa не рекомендуется в пользу inet_ntop.

Собрав все это вместе, вы получите:

char addressBuffer[INET6_ADDRSTRLEN];

for (NSData *data in self.addresses)
{
    memset(addressBuffer, 0, INET6_ADDRSTRLEN);

    typedef union {
        struct sockaddr sa;
        struct sockaddr_in ipv4;
        struct sockaddr_in6 ipv6;
    } ip_socket_address;

    ip_socket_address *socketAddress = (ip_socket_address *)[data bytes];

    if (socketAddress && (socketAddress->sa.sa_family == AF_INET || socketAddress->sa.sa_family == AF_INET6))
    {
        const char *addressStr = inet_ntop(
                socketAddress->sa.sa_family,
                (socketAddress->sa.sa_family == AF_INET ? (void *)&(socketAddress->ipv4.sin_addr) : (void *)&(socketAddress->ipv6.sin6_addr)),
                addressBuffer,
                sizeof(addressBuffer));

        int port = ntohs(socketAddress->sa.sa_family == AF_INET ? socketAddress->ipv4.sin_port : socketAddress->ipv6.sin6_port);

        if (addressStr && port)
        {
            NSLog(@"Found service at %s:%d", addressStr, port);
        }
    }
}
18 голосов
/ 02 июня 2009

NSNetService, который вы возвращаете в обратном вызове, не готов к использованию. Вы должны вызвать следующий метод, чтобы получить адреса для него:

- (void)resolveWithTimeout:(NSTimeInterval)timeout;

Реализуйте метод делегата NSNetService, чтобы узнать, когда он разрешается:

- (void)netServiceDidResolveAddress:(NSNetService *)sender;

На этом этапе в службе должен быть хотя бы один адрес.

Кроме того, внимательно прочитайте документацию и заголовочный файл! Здесь есть некоторая сложность, которую я замутил.

3 голосов
/ 19 сентября 2016

Ремикс принятого ответа в категории:

NSNetService + util.h

#import <Foundation/Foundation.h>

@interface NSNetService (Util)

- (NSArray*) addressesAndPorts;

@end


@interface AddressAndPort : NSObject 

@property (nonatomic, assign) int port;
@property (nonatomic, strong)  NSString *address;

@end

NSNetService + Util.m

#import "NSNetService+Util.h"
#include <arpa/inet.h>

@implementation NSNetService (Util)

- (NSArray*) addressesAndPorts {

    // this came from http://stackoverflow.com/a/4976808/8047
    NSMutableArray *retVal = [NSMutableArray array];
    char addressBuffer[INET6_ADDRSTRLEN];

    for (NSData *data in self.addresses)
    {
        memset(addressBuffer, 0, INET6_ADDRSTRLEN);

        typedef union {
            struct sockaddr sa;
            struct sockaddr_in ipv4;
            struct sockaddr_in6 ipv6;
        } ip_socket_address;

        ip_socket_address *socketAddress = (ip_socket_address *)[data bytes];

        if (socketAddress && (socketAddress->sa.sa_family == AF_INET || socketAddress->sa.sa_family == AF_INET6))
        {
            const char *addressStr = inet_ntop(
                                               socketAddress->sa.sa_family,
                                               (socketAddress->sa.sa_family == AF_INET ? (void *)&(socketAddress->ipv4.sin_addr) : (void *)&(socketAddress->ipv6.sin6_addr)),
                                               addressBuffer,
                                               sizeof(addressBuffer));

            int port = ntohs(socketAddress->sa.sa_family == AF_INET ? socketAddress->ipv4.sin_port : socketAddress->ipv6.sin6_port);

            if (addressStr && port)
            {
                AddressAndPort *aAndP = [[AddressAndPort alloc] init];
                aAndP.address = [NSString stringWithCString:addressStr encoding:kCFStringEncodingUTF8];
                aAndP.port = port;
                [retVal addObject:aAndP];
            }
        }
    }
    return retVal;

}


@end


@implementation AddressAndPort
@end

[Да, я не боюсь создавать множество NSObject экземпляров ...]

...