2012-07-27 74 views
3

我正在制作一个包含两个组件的应用程序,一个iPhone组件和一个Mac组件。他们应该通过bonjour彼此沟通。我使用下面的代码在Mac端找到一个端口的服务:如何在iOS上找到适合NSNetService的端口?

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

我也可以用在iPhone上月底代码并运行在模拟器应用程序,以及连接工作正常。但是,当我试图在真正的iPhone上运行此代码时,我发现NSSocketPort在iPhone上不可用。当我尝试使用端口9876启动服务时,Mac应用程序显示连接拒绝错误,当我尝试连接它时。那么如何在不使用NSSocketPort的情况下找到一个可以使用的端口?

回答

4

好吧,我看着苹果的WiTap代码稍加修改它给自己写一个方法用于获取端口:

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

} 

我拿出了很多错误的东西,所以它可能不是最优的,但它的工作原理。

+1

此代码泄漏'CFSocketRef'。 – jonahb 2014-02-02 16:52:37

+0

这会每次创建一个新的连接或测试存在吗?我每次都收到新的端口。这是对的吗? – landonandrey 2016-03-09 11:16:32

4

从iOS 7开始,NSNetService可以为您创建和绑定套接字。如果与端口初始化为零,它会选择一个随机端口:

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

建立连接时,该服务将发送netService:didAcceptConnectionWithInputStream:outputStream:其委托。

相关问题