iPhone: IP-адрес и порт Bonjour NSNetService

Извините за мой статус новичка в 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 никогда не вводится, даже если вызывается делегат. Любые идеи? Спасибо!


person nikkumang    schedule 02.06.2009    source источник
comment
В качестве побочного комментария цикл for, который у вас есть, считается плохой формой (и медленным). Вы должны использовать расширенный цикл for: for (NSData *addressData in [server addressses]) { …   -  person Andrew Pouliot    schedule 02.06.2009


Ответы (3)


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

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

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

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

  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);
        }
    }
}
person escouten    schedule 12.02.2011
comment
Просто хочу добавить, что я должен #include ‹arpa/inet.h› :) - person dzeikei; 18.10.2011
comment
Вместо 100 вы можете использовать одну из констант INET6_ADDRSTRLEN или INET_ADDRSTRLEN. - person i_am_jorf; 14.11.2012
comment
Этот код не подходит для адресов IPv6. Вы используете структуру sockaddr_in IPv4. Что согласуется с IPv6 для первых двух членов структуры, но не после этого. - person orj; 01.07.2013
comment
Впоследствии я обновил приведенный выше код, чтобы правильно обрабатывать адреса ipv6. - person orj; 01.07.2013
comment
Невероятно полезно, спасибо! Мне так нравится код, что я сделал ремикс в категории ниже. - person Dan Rosenstark; 19.09.2016

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

- (void)resolveWithTimeout:(NSTimeInterval)timeout;

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

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

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

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

person Andrew Pouliot    schedule 02.06.2009

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

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 экземпляров...]

person Dan Rosenstark    schedule 19.09.2016