4

Appleの「ポーリングと実行ループのスケジューリング」によると:

[ hasSpace/BytesAvailable]は、使用可能なバイトまたはスペースがあること、または読み取りまたは書き込み操作を試行することだけを確認する方法(瞬間的なブロックにつながる可能性がある)を意味する場合があります。

ドキュメントには、hasSpace / BytesAvailableイベントが同じように動作することは明示的に記載されていませんが、あいまいなことに、「同一のセマンティクス」があることが示されています。

書き込み/読み取りstreamErrorまたはバイト読み取り/書き込みリターンが予想よりも少ないのは、「瞬間的なブロック」が原因である可能性があると結論付けることができますか?

もしそうなら、私は送信を再試行する必要がありますか?閉塞を解消する機会を与えるために、ある種のタイマーメカニズムを使用する必要がありますか?これは実装するのに多くの作業になるので、役に立たない場合はむしろしたいと思います。

(このような場合、制限付きのポーリングループを開始するのは魅力的です。たとえば、10回試行するwhileループですが、実行ループでストリームがスケジュールされると同時にそれを実行しても安全かどうかはわかりません。テストする方法がありません。)

4

3 に答える 3

1

ソケットの適切なラッパーは次のとおりです:https ://github.com/robbiehanson/CocoaAsyncSocket

接続が利用できない場合は、読み取りと書き込みをキューに入れます。UDPとTCPのどちらを使用しているかについては言及していませんが、TCPを使用していると思われます。その場合、接続が切断されない限り、TCPはそれ自体で中断を処理します。

于 2012-01-06T15:58:54.430 に答える
1

それは長距離でした。この問題に関するフォローアップは次のとおりです。

早い段階で、残りのキャッシュを維持およびチェックするというアイデアを捨てました。これは、入力ストリームもブロックされる可能性があることをさらに反映したときに、出力ストリームに対してのみ機能するためです。

代わりに、アイドリングwhileループを設定しました。

- (void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)eventCode {

    switch (eventCode) 
        // RECEIVING
    case NSStreamEventHasBytesAvailable: {
        if (self.receiveStage == kNothingToReceive)
            return;
        // Get the data from the stream. (This method returns NO if bytesRead < 1.)
        if (![self receiveDataViaStream:(NSInputStream *)theStream]) {
            // If nothing was actually read, consider the stream to be idling.
            self.bStreamIn_isIdling = YES;
            // Repeatedly retry read, until (1) the read is successful, or (2) stopNetwork is called, which will clear the idler.
            // (Just in case, add nil stream property as a loop breaker.) 
            while (self.bStreamIn_isIdling && self.streamIn) {
                if ([self receiveDataViaStream:(NSInputStream *)theStream]) {
                    self.bStreamIn_isIdling = NO;
                    // The stream will have started up again; prepare for next event call.
                    [self assessTransmissionStage_uponReadSuccess];
                }
            }
        }
        else
            // Prepare for what happens next.
            [self assessTransmissionStage_uponReadSuccess];
        break;
        // SENDING
    case NSStreamEventHasSpaceAvailable: 
        if (self.sendStage == kNothingToSend)
            return;
        if (![self sendDataViaStream:(NSOutputStream *)theStream]) {
            self.bStreamOut_isIdling = YES;
            while (self.bStreamOut_isIdling && self.streamOut) {
                if ([self sendDataViaStream:(NSOutputStream *)theStream]) {
                    self.bStreamOut_isIdling = NO;
                    [self assessTransmissionStage_uponWriteSuccess];
                }
            }
        }
        else
            [self assessTransmissionStage_uponWriteSuccess]; 
        break;
    // other event cases…

次に、「キャンセル」ボタンを使用して、ユーザーが開始したキャンセルをテストするときが来ました。同期の途中で、Cocoa側で一時停止があり、ユーザー入力を待機しています。ユーザーがこの時点でキャンセルした場合、Cocoaアプリはストリームを閉じて実行ループから削除するため、接続の反対側のストリームがNSStreamEventEndEncounteredイベントを生成するか、おそらくNSStreamEventErrorOccurred。しかし、いや、たった1つのイベント、NSStreamEventHasBytesAvailable!図に行きます。

もちろん、ストリームは書き込まれずにCocoa側で閉じられていたため、実際には「使用可能なバイト」はありませんでした。そのため、iOS側のストリームハンドラーは無限ループに入りました。そんなに良くない。

次に、デバイスの1つがスリープ状態になった場合にどうなるかをテストしました。ユーザー入力の一時停止中に、自動ロック*を介してiPhoneをスリープ状態にしてから、Cocoa側でユーザー入力を提供しました。もう一度驚いたことに、Cocoaアプリは同期の最後まで混乱することなく継続し、iPhoneを起動すると、iOSアプリも同期の側面を完了したことがわかりました。

私のアイドルループによって修正されたiPhone側の問題があった可能性がありますか?ネットワーク停止ルーチンを投入して、次のことを確認しました。

if (![self receiveDataViaStream:(NSInputStream *)theStream])
    [self stopNetwork]; // closes the streams, etc.

同期はまだ完了しました。障害はありませんでした。

最後に、入力の一時停止中にMac(Cocoa側)がスリープ状態になった場合に何が起こるかをテストしました。これにより、一種の逆げっぷが発生しました。2つのNSStreamEventErrorOccurredイベントが受信されました— Mac側で、その後、出力ストリームに書き込むことができなくなりました。iPhone側ではイベントはまったく受信されませんでしたが、iPhoneのストリームステータスをテストすると、5、NSStreamStatusAtEndが返されます。

結論と計画:

  • 「一時的なブロック」はユニコーンのようなものです。ネットワークがスムーズに動作するか、完全に切断されます。
  • 本当に一時的なブロックのようなものがある場合、それを完全な切断と区別する方法はありません。一時ブロックに対して論理的と思われるストリームステータス定数は、NSStreamStatusAtEndとだけNSStreamStatusErrorです。しかし、上記の実験によれば、これらは切断を示しています。
  • その結果、whileループを破棄し、bytesRead /Written<1をチェックするだけで切断を検出しています。

* Xcodeにスレーブされている場合、iPhoneはスリープしません。iPhoneから直接実行する必要があります。

于 2012-05-12T19:54:38.517 に答える
1

出力ストリームに0バイトを書き込もうとしたとき、または入力ストリームで0バイトを受け取ったときはいつでも、「切断」が予想されます。ストリームを存続させたい場合は、出力ストリームに書き込んでいるバイトの長さを確認してください。そうすれば、入力ストリームが0バイトを受信することはなく、閉じたストリームのイベントハンドラーがトリガーされます。

「アイドリング」出力ストリームのようなものはありません。出力ストリームへのバイトのアイドリングプロバイダーのみ。アイドル状態を示す必要はありません。

スリープタイマーによってネットワーク接続から切断されている場合は、ストリームを開くときに無効にし、ストリームを閉じるときに無効にすることができます。

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
    switch(eventCode) {
        case NSStreamEventOpenCompleted:
        {
            [UIApplication sharedApplication].idleTimerDisabled = YES;
            break;
        }

        case NSStreamEventEndEncountered:
        {
            [UIApplication sharedApplication].idleTimerDisabled = NO;
            break;
        }
    }
}

ストリームが正確に何であるかが完全に明確ではないことをすぐに伝えることができるので、私はあなたの状況の詳細についてこれ以上掘り下げることはしません。私は、ストリームに関するドキュメントが初心者のプライミングに非常に乏しく、起動するのが不十分であることを理解しています。ただし、これらは30年間使用されているものと同じストリームをモデル化しているため、任意のオペレーティングシステム(Windowsを除く)のストリームに関するドキュメントは、速度を上げるのに完全に機能します。

ちなみに、ストリームのもう1つの不可分な部分は、指定しなかったネットワーク接続コードです。NSNetServiceとNSNetServiceBrowserを使用してピアを検索し、それらに接続して、それに応じてストリームを取得していない場合は、そうすることをお勧めします。そうすることで、ネットワーク接続の状態を簡単に監視し、予期せず閉じた場合にストリームをすばやく簡単に再開できます。

私はこれについて非常に徹底的でありながらわかりやすいサンプルコードを持っています。これは、誰かが望むのであれば、あなたの側でカスタマイズする必要はまったくありません。

于 2018-02-26T14:21:48.997 に答える