iOS:使用CFStreamCreatePairWithSocketToHost的套接字网络基础

时间:2012-08-13 19:24:10

标签: ios sockets network-programming

用例

我正在使用套接字来使用CFStreamCreatePairWithSocketToHost()发送和接收数据,并且我试图在发送多个数据时(不仅仅是1 < EM>请求)。

问题

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

问题

所以问题是,当我想发送多个数据请求时会发生什么?

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

代码

此代码大部分来自Cocoa Streams Documentation

- (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的空间可用事件   对象接收更多字节。发生这种情况时,运行循环是   为空间可用事件重新启动。

但是,文档没有说明在到达流末尾时关闭流。所以我很困惑......

1 个答案:

答案 0 :(得分:17)

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

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

连接保持打开,直到一侧关闭插座。还有可能仅在一个方向上关闭插座。也可以仅在一个方向上关闭连接。如果遥控器关闭它的读取流,您的写入流将被关闭,反之亦然。

  

每次要发送新数据对象时,是否设置了新套接字?

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

  

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

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

  

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

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

您引用的文档部分是关于不同的内容。如果您在获得可用空间通知后没有写信,那么在您写完文章之前,您将无法获得另一个通知。这很有用,因为系统不会浪费CPU周期来一遍又一遍地告诉你的代码,如果你没有任何东西可以写,你可以写一些东西。