IPhone: Bonjour NSNetService IP-адрес и порт
Извините мой статус 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 никогда не вводится, хотя делегат вызывается. Есть идеи? Спасибо!
Ответы
Ответ 1
Я понимаю, что это старый поток, но я просто столкнулся с этим. Есть несколько проблем с приведенным выше кодом:
-
Это не IPv6 подкованный. На
минимум, он должен
отбросить адреса IPv6, если остальные
вашего приложения может обрабатывать только v4
адресов, но в идеале вы должны быть
готовы пройти оба адреса
семьи вверх по течению.
-
Назначение порта будет
генерировать неверные значения для Intel
процессоры. Вам нужно использовать htons
чтобы исправить это.
-
Как отметил Андрей,
итерация должна использовать расширенные
для цикла.
-
(EDIT: Добавлено это) Как отмечено в другом связанном потоке, использование 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);
}
}
}
Ответ 2
NSNetService, который вы возвращаете в обратном вызове, не готов к использованию. Вы должны вызвать следующий метод для получения адресов для него:
- (void)resolveWithTimeout:(NSTimeInterval)timeout;
Внедрите метод делегата NSNetService, чтобы узнать, когда он разрешится:
- (void)netServiceDidResolveAddress:(NSNetService *)sender;
В этот момент в службе должен быть хотя бы один адрес.
Кроме того, внимательно прочитайте документацию и файл заголовка! Здесь есть определенная сложность в том, что я замалчивался.
Ответ 3
Ремикс принятого ответа в категории:
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
...]