6

使用事例

私はソケットを使用してデータを送受信し、複数のデータセット(つまり、1つのリクエストCFStreamCreatePairWithSocketToHost()だけでなく)を送信するときにこれがどのように行われるかについて頭を悩ませようとしています。

問題

現在、データを送信して応答を受信できます(つまり、1往復)。ただし、ストリーム内のすべてのデータを送信するとoutputStream、ストリームは閉じられます(つまり、受信しますNSStreamEventEndEncountered)。

質問

したがって、問題は、複数のデータ要求を送信したい場合はどうなるかということです。

  • 送信する新しいデータオブジェクトがあるたびに新しいソケットを設定しますか?
  • リセット outputStreamしてさらにデータを送信する必要がありますか?

コード

このコードのほとんどは、CocoaStreamsのドキュメントからのものです。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    _data = [[NSMutableData alloc] init];
    [self initNetworkCommunication];

    [self sendString:@"Hello World!"];
}

- (void)initNetworkCommunication {
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"123.456.0.0", 1234, &readStream, &writeStream);

    inputStream = (NSInputStream *)readStream; // ivar
    [inputStream setDelegate:self];
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inputStream open];

    outputStream = (NSOutputStream *)writeStream; // ivar
    [outputStream setDelegate:self];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream open];
}

- (void)sendString:(NSString *)string {
    NSData *data = [[NSData alloc] initWithData:[string dataUsingEncoding:NSASCIIStringEncoding]];
    [_data appendData:data];
    [data release];
}

- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
    NSLog(@"stream event %u", streamEvent);

    switch (streamEvent) {
        case NSStreamEventOpenCompleted:
            NSLog(@"Stream opened");
            break;
        case NSStreamEventHasSpaceAvailable: {
            uint8_t *readBytes = (uint8_t *)[_data mutableBytes];
            readBytes += byteIndex; // ivar
            int data_len = [_data length];
            unsigned int len = ((data_len - byteIndex >= 1024) ? 1024 : (data_len - byteIndex));
            uint8_t buf[len];
            (void)memcpy(buf, readBytes, len);
            len = [(NSOutputStream *)theStream write:(const uint8_t *)buf maxLength:len];
            NSLog(@"Sending buffer of len: %d", len);
            byteIndex += len;
            break;
        }
        case NSStreamEventHasBytesAvailable:
            if (theStream == inputStream) {
                uint8_t buffer[1024];
                int len;

                while ([inputStream hasBytesAvailable]) {
                    len = [inputStream read:buffer maxLength:sizeof(buffer)];
                    if (len > 0) {
                        NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];

                        if (nil != output) {
                            NSLog(@"server said: %@", output);
                        }
                    }
                }

                [self sendString:@"Another Test"];
            }
            break;
        case NSStreamEventErrorOccurred:
            NSLog(@"Can not connect to the host!");
            break;
        case NSStreamEventEndEncountered:
            NSLog(@"Closing stream...");
            [theStream close];
            [theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [theStream release];
            theStream = nil;
            break;
        default:
            NSLog(@"Unknown event");
    }
}

応答:

2012-08-15 08:16:30.896 Sockets[34836:f803] Opened input stream.
2012-08-15 08:16:30.898 Sockets[34836:f803] Opened output stream.
2012-08-15 08:16:30.899 Sockets[34836:f803] Sending buffer of len: 12
2012-08-15 08:16:30.900 Sockets[34836:f803] Sending buffer of len: 0
2012-08-15 08:16:30.901 Sockets[34836:f803] Closing output stream.
2012-08-15 08:16:30.939 Sockets[34836:f803] server said: Hello World!

データを送信した後、outputStreamストリームが閉じることに注意してください。outputStream前に再開してみ[self sendString:@"Another Test"];ます。idzの答えも試してみました。

ドキュメントによると、len:0の送信バッファが私の問題だと思います。

デリゲートがNSStreamEventHasSpaceAvailableイベントを受信し、ストリームに何も書き込まない場合、NSOutputStreamオブジェクトがさらにバイトを受信するまで、実行ループからそれ以上のスペース使用可能イベントを受信しません。これが発生すると、空き領域のイベントに対して実行ループが再開されます。

ただし、ドキュメントには、ストリームの終わりに達したときにストリームを閉じることについては何も記載されていません。だから私は混乱しています…</p>

4

1 に答える 1

19

ソケットは、2つのプログラムを接続する双方向ストリームであり、2つの異なるコンピューター上にある可能性があります。一方の端で書き込んだデータを転送して、もう一方の端で読み取るだけです。データに構造を強制せず、要求または応答について何も知りません。

APIはCFStreamCreatePairWithSocketToHost、単一の接続を2つの独立したストリームに分割します。1つは読み取り可能で、もう1つは書き込み可能です。これはいい感じです。基盤となるソケットAPIは、読み取りと書き込みの両方に1つのファイル記述子のみを使用するため、非常に混乱する可能性があります。

片側がソケットを閉じるまで、接続は開いたままになります。ソケットを一方向にのみシャットダウンする可能性もあります。一方向のみの接続を閉じることも可能です。リモートが閉じると、読み取りストリームが閉じられ、書き込みストリームも閉じられます。その逆も同様です。

送信する新しいデータオブジェクトがあるたびに新しいソケットを設定しますか?

あなたはそれを避けるべきです。新しい接続を確立するには時間がかかり、接続がフルスピードになるまでにはさらに時間がかかります。したがって、可能な限り同じ接続を再利用する必要があります。

outputStreamをリセットして、さらにデータを送信する必要がありますか?

いいえ、これは必要ありません。より多くのデータを送信するだけです。

ドキュメントによると、len:0の送信バッファが私の問題だと思います。

何も書き込まない(つまり、長さ0のバッファー)ことは問題にはなりません。ただし、ドキュメントには何が起こるかは指定されていません。それで、私は今日、何も期待せずに何が起こるかを見るためにプログラムをテストすることを書きました。長さ0のバッファーを書き込むと、出力ストリームが閉じます。だからこれは本当にあなたの問題でした。そのドキュメントの問題についてAppleBugReporterにバグを報告します。

あなたが引用したドキュメントの一部は、何か違うものについてです。空き容量の通知を受け取った後に書き込みを行わないと、何かを書き込むまで別の通知を受け取ることはありません。これは便利です。システムがCPUサイクルを浪費して、何も書くことがなければ何かを書くことができることをコードに何度も伝えることがないからです。

于 2012-08-21T21:23:06.090 に答える