简体   繁体   English

如何在iOS上找到适合NSNetService的端口?

[英]How to find proper port for NSNetService on iOS?

I am making an app with two components, an iPhone component and a Mac component. 我正在制作一个包含两个组件的应用程序,一个iPhone组件和一个Mac组件。 They are supposed to communicate with each other via bonjour. 他们应该通过bonjour互相沟通。 I use the following code on the Mac end to find a port for the service: 我在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!!!");
}

I can also use this code on the iPhone end and run the app in the simulator, and the connection works fine. 我也可以在iPhone端使用此代码并在模拟器中运行应用程序,连接工作正常。 However, when I tried to run this code on an actual iPhone, I discovered that NSSocketPort is not available on the iPhone. 但是,当我尝试在实际的iPhone上运行此代码时,我发现iPhone上没有NSSocketPort。 When I try to start the service with the port 9876, the Mac app displays a connection refused error when I try to connect with it. 当我尝试使用端口9876启动服务时,Mac应用程序在尝试连接时显示连接被拒绝错误。 So how can I find a port to use without using NSSocketPort? 那么如何在不使用NSSocketPort的情况下找到要使用的端口?

Ok I looked at Apple's WiTap code and slightly modified it to write myself a method for getting a port: 好的,我查看了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);
}

}

I took out a lot of the error stuff so it's probably not optimal, but it works. 我拿出了很多错误的东西,所以它可能不是最佳的,但它可以工作。

As of iOS 7, NSNetService can create and bind the socket for you. 从iOS 7开始,NSNetService可以为您创建和绑定套接字。 If initialized with port zero, it will choose a random port: 如果使用端口0初始化,它将选择一个随机端口:

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

The service will send netService:didAcceptConnectionWithInputStream:outputStream: to its delegate when a connection is established. 该服务将在建立连接时将netService:didAcceptConnectionWithInputStream:outputStream:发送到其委托。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM