2012-04-30 4 views
5

iPhone과 Mac간에 Bonjour 네트워크를 설정했습니다.bytesWritten,하지만 다른 장치는 NSStreamEventHasBytesAvailable 이벤트를 수신하지 않습니다.

사용자가 Mac에서 제공되는 테이블에서 iPhone의 인터넷 서비스를 선택하면 한 쌍의 스트림이 만들어지고 양쪽에서 열립니다.

iPhone은 코드 (정수)를 Mac으로 전송하여 시작합니다. Mac이 성공적으로 수신합니다.

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

을하지만 아이폰은 결코 NSStreamEventHasBytesAvailable 이벤트를 얻을 수 없습니다 : 사용자 입력 및 처리를 위해 일시 ​​정지 후

는 맥 아이폰에 코드를 전송하기 시작합니다. 이 시점 직전에 double-check를했고, iPhone의 NSInputStream에있는 streamStatus는 NSStreamStatusOpen 인 2입니다.

어떤 아이디어가 잘못되었을 수 있습니까?


업데이트 : Mac을 iPhone으로 정수를 전송 한 최초의 테스트를 실행했습니다. 다시 말하지만, Mac의 출력 스트림에서 1의 bytesWritten을 얻었지만 iPhone에는 NSStreamEventHasBytesAvailable 이벤트가 없었습니다.

그래서 iPhone의 입력 스트림에 문제가있을 것입니다. 하지만 doublechecked :

  • 아이폰의 self.streamIn가 제대로
  • 아이폰 2 개 NSStreamEventOpenCompleted 이벤트를 수신하는 시간 파일에 NSInputStream로 입력, 나는 스트림 인수의 클래스를 확인한다. 하나는 KindOfClass : [NSOutputStream 클래스], 다른 하나는 그렇지 않습니다.
  • iPhone은 NSStreamEventEndEncountered, NSStreamEventErrorOccurred 또는 NSStreamEventNone을 수신하지 않습니다.
  • 위에서 언급했듯이 Mac의 출력 스트림에 기록한 다음 iPhone의 입력 스트림 상태는 2입니다. NSStreamStatusOpen입니다.

다음은 iPhone의 입력 스트림을 만드는 데 사용되는 코드입니다. 그것이 C 스타일의 소켓 콜백 함수에서 수행 있기 때문에 그것은 CF 유형을 사용 :

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

갱신 2 :

소켓 옵션

유지, 릴리스 : 알라의 코멘트에 대한 정보는 응답 copyDescription 콜백은 NULL로 설정됩니다. optionFlags는 acceptCallback으로 설정됩니다.

소켓 생성 여기

아이폰과 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, 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, yes, 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, 0, 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은 정상적으로 실행되는 것처럼 보입니다.


업데이트 4, 스트림 포트? 내가 스트림 자신의 포트에 매달려 있었다,하지만 놀랍게도, nTest는 항상 null이라고 가정했다

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

:

노아의 디버깅 제안

더 스트림의 속성을 검사하는 나에게 아이디어를 주었다. 그것은 내 애플 리케이션에서 null입니다. 문제를 지적하는 것처럼 보이지만, 인 튜토리얼 앱에서도 null입니다. 일단 스트림이 만들어지면 포트 할당에 매달릴 필요가 없으면 포트 속성의 목적은 무엇입니까?

아마도 port 속성에 직접 액세스 할 수 없습니까? 그러나 nTest도 다음에 항상 널 :

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) 
+2

당신이'-hasBytesAvailable'와'- 읽기를 호출 해봤'직접? – paulmelnikow

+0

iPhone에서 [self.streamIn hasBytesAvailable]을 호출하면 Mac이 바이트를 쓴다는 것을 Mac 응용 프로그램 로그에서 확인한 후에도 테스트 버튼 액션을 통해 * 호출한다고해도 NO가 반환됩니다. 우수한 디버깅 조언, 아니 반환과 함께 나는 다음에 무엇을해야할지 모르겠다. – Wienke

+1

어떤 소켓 옵션을 설정 했습니까? 이러한 스트림을 사용하여 소켓을 어떻게 만들었습니까? 모든 스트림이 예정대로 운영되고 있습니까? 문제의 실행 루프가 실제로 실행되고 있습니까 (예 : 아무 곳이나 차단하지 않습니까?) – alastair

답변

2

문제는이 줄을이었다 : 만 아이폰 끝에 데이터를 수신 한 경우

CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketNativeHandle, &readStream, NULL); 

이 확인했을 것이다. 하지만 스트림 를 생성하고,뿐만 아니라 입력 스트림이 코드 아래에 내가 쓰기 스트림 생성 그래서 : 당신이 [readStream에 대한] NULL을 전달하면

CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketNativeHandle, NULL, &writeStream); 

CFStream 참조는 말한다, "을, 이 함수는 읽을 수있는 스트림을 생성하지 않을 것입니다. "NULL을 전달하면 이전에 생성 된 스트림을 작동 불능으로 렌더링한다고 말할 수 없습니다. 그러나 그것은 분명히 일어난 일입니다.

이 설정의 이상한 유물은 streamIn을 먼저 열면 반대의 문제가 발생한다는 것입니다. iPhone은 hasByteAvailable 이벤트를 갖지만 hasSpaceAvailable 이벤트는 발생하지 않습니다. 그리고 질문에 언급했듯이 스트림 상태를 질의하면 NSStreamStatusOpen이 리턴됩니다. 따라서 실제 실수가 어디에서 발생했는지 파악하는 데는 오랜 시간이 걸렸습니다.

(이 순차적 스트림 생성은 몇 달 전에 설정 한 테스트 프로젝트의 아티팩트이며 한 방향 또는 다른 방향으로 만 데이터를 테스트했습니다.)

해결 한 줄에

한 쌍으로 작성해야 두 스트림 : 최대 길이 :

CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketNativeHandle, &readStream, &writeStream); 
+0

정답으로 자신의 답변을 표시하십시오. 그것을 알아내는 것에 좋은 직업. –