6

用例

我正在使用套接字来发送和接收数据,并试图在发送多数据(即不仅仅是 1 个请求CFStreamCreatePairWithSocketToHost()时将我的头脑围绕在如何完成这一点上。

问题

目前我可以发送数据并接收响应(即 1 次往返)。但是,在我发送流中的所有数据后,outputStream流被关闭(即接收NSStreamEventEndEncountered)。

问题

那么问题来了,当我想发送多个数据请求时会发生什么?

  • 每次我有一个新的数据对象要发送时,我是否设置一个新的套接字?
  • 我是否必须重置 outputStream并发送更多数据。

代码

大部分代码来自Cocoa Streams 文档

- (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

套接字是连接两个程序的双向流,可能在两台不同的计算机上。它只是将您在一端写入的数据传输到另一端读取。它在数据中不强制执行任何结构,并且对请求或响应一无所知。

API将CFStreamCreatePairWithSocketToHost单个连接拆分为两个独立的流——一个可以读取,一个可以写入。这是一个很好的接触,底层的套接字 API 只使用一个文件描述符来读取和写入,这可能会让人很困惑。

连接保持打开状态,直到一侧关闭套接字。也有可能只在一个方向上关闭插座。也可以只在一个方向上关闭连接。如果远程关闭它的读取流,您的写入流将被关闭,反之亦然。

每次我有一个新的数据对象要发送时,我是否设置一个新的套接字?

你应该避免这样做。建立新连接需要一些时间,而且连接全速需要更多时间。所以你应该尽可能地重用相同的连接。

我是否必须重置 outputStream 并发送更多数据。

不,这不是必需的,只需发送更多数据即可。

根据文档,我相信 len: 0 的发送缓冲区是我的问题。

什么都不写(即长度为 0 的缓冲区)应该不是问题。文档没有说明会发生什么。所以我今天写了我的测试程序,看看会发生什么,什么都不期待。事实证明,写入长度为 0 的缓冲区会关闭输出流。所以这真的是你的问题。我将在Apple Bug Reporter上提交有关该文档问题的错误,您也应该这样做。

您引用的文档部分是关于不同的。如果您在收到可用空间通知后不写,则在您写完之前不会收到另一个通知。这很有用,因为系统不会浪费 CPU 周期来一遍又一遍地告诉你的代码,如果你没有什么可写的,你可以写一些东西。

于 2012-08-21T21:23:06.090 回答