ios – bytesWritten,但其他设备从不接收NSStreamEventHasBytesAvailable事件

ios – bytesWritten,但其他设备从不接收NSStreamEventHasBytesAvailable事件,第1张

概述我在iPhone和Mac之间建立了一个Bonjour网络. 用户在Mac中呈现的表格中选择iPhone的网络服务,并在两侧创建并打开一对流. iPhone首先向Mac发送代码(整数). Mac成功收到它. 暂停用户输入和处理后,Mac会启动向iPhone发送代码: NSInteger bytesWritten = [self.streamOut write:buffer maxLength:siz 我在iPhone和Mac之间建立了一个Bonjour网络.

用户在Mac中呈现的表格中选择iPhone的网络服务,并在两侧创建并打开一对流.

iPhone首先向Mac发送代码(整数). Mac成功收到它.

暂停用户输入和处理后,Mac会启动向iPhone发送代码:

NSInteger bytesWritten = [self.streamOut write:buffer maxLength:sizeof(uint8_t)];// bytesWritten is 1.

但iPhone永远不会获得NsstreamEventHasBytesAvailable事件.我在此之前仔细检查了一下,iPhone的NSinputStream上的streamStatus是2,它应该是NsstreamStatusOpen.

什么想法可能是错的?

更新:我运行了一个测试,其中Mac是第一个向iPhone发送整数的测试.再一次,我从Mac的输出流中获得了1的bytesWritten,但iPhone从未得到过NsstreamEventHasBytesAvailable事件.

所以iPhone的输入流肯定有问题.但我仔细检查:

> iPhone的self.streamIn在h文件中正确输入为NSinputStream
> iPhone接收2个NsstreamEventopenCompleted事件,并检查流arg的类.一个是KindOfClass:[NSOutputStream类],另一个不是.
> iPhone永远不会收到NsstreamEventEndEncountered,NsstreamEventErrorOccurred或NsstreamEventNone.
>如上所述,在Mac写入输出流之后,iPhone的输入流状态为2,即NsstreamStatusOpen.

这是用于创建iPhone输入流的代码.它使用CF类型,因为它是在C风格的套接字回调函数中完成的:

CFReadStreamRef readStream = NulL;CFStreamCreatePairWithSocket(kcfAllocatorDefault,socketNativeHandle,&readStream,NulL);if (readStream) {    CFReadStreamSetProperty(readStream,kcfStreamPropertyShouldCloseNativeSocket,kcfBooleanTrue);    server.streamIn = (NSinputStream *)readStream;    server.streamIn.delegate = server;    [server.streamIn scheduleInRunLoop:[NSRunLoop currentRunLoop]                                forMode:NSDefaultRunLoopMode];    if ([server.streamIn streamStatus] == NsstreamStatusNotopen)        [server.streamIn open];    CFRelease(readStream);}

Update2:响应alastair评论的信息:

套接字选项

retain,release和copyDescription回调设置为NulL. optionFlags设置为acceptCallback.

套接字创建

这是用于在iPhone和Mac上设置套接字的方法,完成了我的评论尝试,以找出此代码中实际发生的事情,该代码改编自各种教程和实验(有效):

/** Socket creation,port assignment,socket scheduled in run loop. The socket represents the port on this app's end of the connection. */- (BOol) makeSocket {    // Make a socket context,with which to configure the socket.    // It's a struct,but doesn't require "struct" prefix -- because typedef'd?CFSocketContext socketCtxt = {0,self,NulL,NulL}; // 2nd arg is pointer for callback function       // Make socket.    // Sock stream goes with TCP protocol,the safe method used for most data transmissions.    // kcfSocketAcceptCallBack accepts connections automatically and presents them to the callback function supplIEd in this class ("acceptSocketCallback").    // CFSocketCallBack,the callback function itself.    // And note that the socket context is passed in at the end.    self.socket = CFSocketCreate(kcfAllocatorDefault,PF_INET,SOCK_STREAM,IPPROTO_TCP,kcfSocketAcceptCallBack,(CFSocketCallBack)&acceptSocketCallback,&socketCtxt);    // Do socket-creation error checking.    if (self.socket == NulL) {        // alert omitted        return NO;    }    // Prepare an int to pass to setsockopt function,telling it whether to use the option specifIEd in arg 3.    int iSocketoption = 1; // 1 means,yes,use the option    // Set socket options.    // arg 1 is an int. C-style method returns native socket.    // arg 2,int for "level." Sol_SOCKET is standard.    // arg 3,int for "option name," which is "uninterpreted." SO_REUSEADDR enables local address reuse. This allows a new connection even when a port is in wait state.    // arg 4,voID (wildcard type) pointer to iSocketoption,which has been set to 1,meaning,use the SO_REUSEADDR option specifIEd in arg 3.    // args 5,the size of iSocketoption,which can Now be recycled as a buffer to report "the size of the value returned," whatever that is.     setsockopt(CFSocketGetNative(socket),Sol_SOCKET,SO_REUSEADDR,(voID *)&iSocketoption,sizeof(iSocketoption));    // Set up a struct to take the port assignment.    // The IDentifIEr "addr4" is an allusion to IP version 4,the older protocol with fewer addresses,which is fine for a LAN.    struct sockaddr_in addr4;    memset(&addr4,sizeof(addr4));    addr4.sin_len = sizeof(addr4);    addr4.sin_family = AF_INET;    addr4.sin_port = 0; // this is where the socket will assign the port number    addr4.sin_addr.s_addr = htonl(INADDR_ANY);    // Convert to NSData so struct can be sent to CFSocketSetAddress.    NSData *address4 = [NSData dataWithBytes:&addr4 length:sizeof(addr4)];    // Set the port number.    // Struct still needs more processing. CFDataRef is a pointer to CFData,which is toll-free-brIDged to NSData.    if (CFSocketSetAddress(socket,(CFDataRef)address4) != kcfSocketSuccess) {        // If unsuccessful,advise user of error (omitted)…        // ... and discard the useless socket.        if (self.socket)             CFRelease(socket);        self.socket = NulL;        return NO;    }    // The socket Now has the port address. Extract it.    NSData *addr = [(NSData *)CFSocketcopyAddress(socket) autorelease];    // Assign the extracted port address to the original struct.    memcpy(&addr4,[addr bytes],[addr length]);    // Use "network to host short" to convert port number to host computer's endian order,in case network's is reversed.    self.port = ntohs(addr4.sin_port);    printf("\nUpon makeSocket,the port is %d.",self.port);// !!!:testing - always prints a 5-digit number    // Get reference to main run loop.    CFRunLoopRef cfrl = CFRunLoopGetCurrent();    // Schedule socket with run loop,by roundabout means.    CFRunLoopSourceRef source4 = CFSocketCreateRunLoopSource(kcfAllocatorDefault,socket,0);    CFRunLoopAddSource(cfrl,source4,kcfRunLoopCommonModes);    CFRelease(source4);    // Socket made    return YES;}

Runloop调度

是的,所有4个流都在runloop中安排,所有使用的代码都相当于我在上面的第一个更新中发布的代码.

Runloop阻止:

我没有做任何与同步,多线程,NSLocks等有关的事情.如果我设置一个按钮动作来打印到控制台的东西,它可以在整个过程中运行 – runloop似乎正常运行.

Update4,Stream Ports?

Noa的调试建议让我有了进一步检查流属性的想法:

NSNumber *nTest = [self.streamIn propertyForKey:NsstreamSOCKSProxyPortKey]; // always null!

我曾假设流被挂在他们的端口上,但令人惊讶的是,nTest始终为空.它在我的应用程序中是空的,这似乎指向一个问题 – 但它在一个有效的教程应用程序中也是null.如果流创建后不需要挂起其端口分配,那么端口属性的目的是什么?

也许港口物业不能直接进入?但是nTest在下面也总是为null:

NSDictionary *dTest = [theInStream propertyForKey:NsstreamSOCKSProxyConfigurationKey];    NSNumber *nTest = [dTest valueForKey:NsstreamSOCKSProxyPortKey];    NSLog(@"\tInstream port is %@.",nTest); // (null)    nTest = [dTest valueForKey:NsstreamSOCKSProxyPortKey];    NSLog(@"\tOutstream port is %@.",nTest); // (null)
解决方法 问题是这一行:

CFStreamCreatePairWithSocket(kcfAllocatorDefault,NulL);

如果我只在iPhone端接收数据,那就没问题.但是我创建了一对流,而不仅仅是输入流,所以在这段代码下我创建了一个写入流:

CFStreamCreatePairWithSocket(kcfAllocatorDefault,&writeStream);

CFStream Reference说,“如果你传递NulL [for readStream],这个函数将不会创建一个可读的流.”它没有说,如果你传递NulL,你将渲染以前创建的流不可 *** 作.但这显然发生了什么.

这个设置的一个奇怪的工件是,如果我首先打开streamIn,我会遇到相反的问题:iPhone将获得hasByteAvailable事件,但从未发生过hasspaceAvailable事件.正如问题所述,如果我查询了流的状态,两者都会返回NsstreamStatusOpen.所以花了很长时间才弄清楚真正的错误在哪里.

(这个顺序流创建是我几个月前建立的测试项目的工件,我测试的数据只在一个方向或另一个方向上移动.)

两个流应该在一行中创建为一对:

CFStreamCreatePairWithSocket(kcfAllocatorDefault,&writeStream);
总结

以上是内存溢出为你收集整理的ios – bytesWritten,但其他设备从不接收NSStreamEventHasBytesAvailable事件全部内容,希望文章能够帮你解决ios – bytesWritten,但其他设备从不接收NSStreamEventHasBytesAvailable事件所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/web/1010088.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-22
下一篇 2022-05-22

发表评论

登录后才能评论

评论列表(0条)

保存