【问题标题】:How to find proper port for NSNetService on iOS?如何在 iOS 上为 NSNetService 找到合适的端口?
【发布时间】:2012-07-27 14:25:37
【问题描述】:

我正在制作一个包含两个组件的应用程序,一个 iPhone 组件和一个 Mac 组件。他们应该通过 bonjour 相互交流。我在 Mac 端使用以下代码来查找服务的端口:

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 的情况下找到要使用的端口呢?

【问题讨论】:

    标签: iphone macos networking bonjour nssocketport


    【解决方案1】:

    好的,我查看了 Apple 的 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);
    }
    
    }
    

    我删除了很多错误内容,所以它可能不是最佳的,但它确实有效。

    【讨论】:

    • 这每次都会创建一个新连接或测试现有连接?我每次都接收新端口。对吗?
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2010-10-30
      • 1970-01-01
      • 1970-01-01
      • 2021-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-22
      相关资源
      最近更新 更多