2012-04-30 4 views
5

iPhoneとMacの間でBonjourネットワークを設定しました。bytesWritten、他のデバイスはNSStreamEventHasBytesAvailableイベントを受信しません

ユーザーは、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の入力ストリームに問題があるはずです。しかし、私はdoublechecked:

  • iPhoneのself.streamInが正しく
  • iPhoneは2つのNSStreamEventOpenCompletedイベントを受け取る時間ファイルにNSInputStreamとして入力し、私は、ストリーム引数のクラスを確認しています。 1つはisKindOfClass:[NSOutputStreamクラス]、もう1つはそうではありません。
  • iPhoneはNSStreamEventEndEncountered、NSStreamEventErrorOccurred、またはNSStreamEventNoneを受け取ることはありません。
  • 上記のように、Macの出力ストリームへの書き込みに続いて、iPhoneの入力ストリームステータスは2、NSStreamStatusOpenです。

ここは、iPhoneの入力ストリームを作成するためのコードです。

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:アラステアさんのコメントに応答情報:

ソケットオプション

保持、解除をそれはCスタイルのソケットコールバック関数の中で行われていますので、それはCFタイプを使用しています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, 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; 
} 

実行ループのスケジューリング

はい、すべての4つのストリームがありますrunloopでスケジュールされていますが、すべて私が上記の最初のアップデートで投稿したものと同等のコードを使用しています。

実行ループのブロッキング:私は同期、複数のスレッド、NSLocks、などと空想何もしていないよ

。コンソールに何かを印刷するためのボタンアクションを設定した場合、それは全体を通して機能します。実行ループは正常に動作しているようです。


Update4、Stream Ports?

ノアのデバッグ提案はさらに、ストリームのプロパティを調べるために私のアイデアを与えた:

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

私はストリームがそのポート上にぶら下がっていたと仮定していたが、意外にも、nTestは常にnullです。私のアプリではnullですが、これは問題を指しているようですが、のチュートリアルアプリでもnullです。ストリームが作成された後のポート割り当てにハングする必要がない場合、ポートプロパティの目的は何ですか?

portプロパティに直接アクセスできない場合がありますか? 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) 
+2

あなたは '-hasBytesAvailable'と' -readを呼び出してみました:maxLengthのを: '直接? – paulmelnikow

+0

iPhone上で[self.streamIn hasBytesAvailable]を呼び出すと、MacのアプリケーションログでMacがバイトを書き込んだことを確認した後に、(テストボタン操作で)*呼び出してもNOが返されます。優れたデバッグのアドバイス、いいえ、私は次に何をすべきかわかりません... – Wienke

+1

ソケットオプションはありますか?どのようにこれらのストリームで使用しているソケットを作成しましたか?すべてのストリームは実行ループでスケジュールされていますか?問題の実行ループが実際に実行されていますか(つまり、どこにもブロックされていませんか?) – alastair

答えて

2

トラブルは、この行だった:

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

私はiPhone側でデータを受信した場合、これはOKだっただろう。私は、ライトストリームを作成していた、このコードの下ので、しかし、私は、ストリームのペアだけでなく、入力ストリームを作成していた。

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

あなたは[readStream用] NULLを渡すとCFStreamリファレンス」は、言います、この関数は読み込み可能なストリームを作成しません。 "あなたがNULLを渡すと、以前に作成されたストリームを動作不能にレンダリングするとは言わない。しかし、それは明らかに何が起こるかです。

この設定の奇妙なアーティファクトの1つは、ストリームを最初に開いた場合、私は反対の問題を抱えているということでした。iPhoneはhasByteAvailableイベントを取得しますがhasSpaceAvailableイベントは発生しませんでした。質問に記載されているように、ストリームのステータスを照会すると、両方ともNSStreamStatusOpenが返されます。実際のミスがどこにあるかを理解するまでには長い時間がかかりました。

(このシーケンシャルストリームの作成は、私が数か月前に設定したテストプロジェクトの成果物であり、データを一方向または他の方向にのみテストしたものです。)

両方のストリームが1行で、ペアとして作成する必要があります

SOLUTION:

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

あなた自身の答えに正しい答えとしてフラグを立ててください。それを見つけ出すのはいい仕事です。 –

関連する問題