用户在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事件所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)