编写`NSOutputStream`的问题。

时间:2011-08-12 09:48:13

标签: cocoa macos objective-c++ nsstream

我有一个基本问题, 在使用NSOutputStream时,我们应该等待NSStreamEventHasSpaceAvailable发送数据包,以便我们可以在需要时调用[NSOutputStream write]

我相信NSStream应该照顾写函数......

如果这不正确,请提供您对以下逻辑的看法,

=====写在NSOutputStream ================= 有队列添加要发送的数据包     // StreamQueue.h

@interface StreamQueue : NSObject <NSCoding>
{
    NSMutableArray * data;
    NSRecursiveLock * theLock;
}

#pragma mark �Initialization & Deallocation�
- (id)init;
- (id)initWithQueue:(CommQueue *)queue;
- (id)initWithCoder:(NSCoder *)coder;
- (void)dealloc;
- (void)encodeWithCoder:(NSCoder *)coder;

#pragma mark
#pragma mark �Accessor Methods�
- (int)size;
- (BOOL)isEmpty;
- (id)top;
- (NSArray *)data;

#pragma mark
#pragma mark �Modifier Methods�
- (void)enqueue:(id)object;
- (id)dequeue;
- (void)removeAll;
@end

及其实施

#import "StreamQueue.h"


@implementation StreamQueue
#pragma mark �Initialization & Deallocation�
- (id)init
{
    if (self = [super init]) {
        data = [[NSMutableArray alloc] init];
        theLock = [[NSRecursiveLock alloc] init];
    }
    return self;
}

- (id)initWithQueue:(StreamQueue *)queue
{
    if (self = [super init]) {
        data = [[NSMutableArray alloc] initWithArray:[queue data]];
        theLock = [[NSRecursiveLock alloc] init];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)coder
{
    if (self = [super init]) {
        data = [[NSMutableArray alloc] initWithArray:[coder decodeObject]];
        theLock = [[NSRecursiveLock alloc] init];
    }
    return self;
}

- (void)dealloc
{
    [data release];
    [theLock release];
    [super dealloc];
}

- (void)encodeWithCoder:(NSCoder *)coder;
{
    [coder encodeObject:data];
}

#pragma mark
#pragma mark �Accessor Methods�
- (int)size
{
    int size;
    [theLock lock];
    size = [data count];
    [theLock unlock];
    return size;
}

- (BOOL)isEmpty
{
    BOOL empty;
    [theLock lock];
    empty = ([data count] == 0);
    [theLock unlock];
    return empty;
}

- (id)top
{
    id object = nil;
    [theLock lock];
    if (![self isEmpty])
        object = [data objectAtIndex:0];
    [theLock unlock];
    return object;
}

- (NSArray *)data
{
    NSArray * array;
    [theLock lock];
    array = [NSArray arrayWithArray:data];
    [theLock unlock];
    return array;
}

#pragma mark
#pragma mark �Modifier Methods�
- (void)enqueue:(id)object
{
    [theLock lock];
    [data addObject:object];
    [theLock unlock];
}

- (id)dequeue
{
    id object = [self top];
    if (object != nil) {
        [theLock lock];
        [object retain];
        [data removeObjectAtIndex:0];
        [theLock unlock];
    }
    return [object autorelease];
}

- (void)removeAll
{
    [theLock lock];
    while (![self isEmpty])
        [data removeObjectAtIndex:0];
    [theLock unlock];
}
@end

现在当Application通过套接字(NSStream)发送内容时,它应该将它添加到队列中,

-(bool)sendRawData:(const uint8_t *)data length:(int)len{

    // if still negotiating then don't send data
    assert(!networkConnected);

    NSData *pData  = [NSData dataWithBytes:(const void *)data length:len];

    // pToSendPacket is of type StreamQueue 
    [pToSendPacket enqueue:pData];

    return;
}
当我们得到NSHasSpaceAvailableEvent

时,

和这段代码

-(void)gotSpaceAvailable{
    // is there any pending packets that to be send. 
    NSData *pData = (NSData *)[pToSendPacket dequeue];

    if(pData == nil){
        // no pending packets.. 
        return;
    }

    const uint8_t *data = (const uint8_t *)[pData bytes];
    int len = [pData length];

    int sendlength = [pOutputStream write:data maxLength:len];

    if(sendlength == -1 ){
        NSError *theError = [pOutputStream streamError];
        NSString *pString = [theError localizedDescription];
        int errorCode = [theError code];
        return ;
    }
}

我希望应用程序会在OutputStream发送数据时继续接收事件,但我只收到一次...... :( 请帮忙......

1 个答案:

答案 0 :(得分:8)

如果您不等待事件,则写入调用将阻塞,直到空间可用。通常,您希望设计代码以便异步工作,因此等待NSStreamEventHasSpaceAvailable是最佳解决方案。

至于何时收到可用空间通知,see the documentation here

  

如果委托收到NSStreamEventHasSpaceAvailable事件并且   不向流写入任何内容,它不会进一步接收   从运行循环到NSOutputStream的空间可用事件   对象接收更多字节。发生这种情况时,运行循环是   重新启动空间可用事件。如果可能出现这种情况   你的实现,你可以让委托设置一个标志   收到后不会写入流   NSStreamEventHasSpaceAvailable事件。以后,当你的程序有   要写入更多字节,它可以检查此标志,如果设置,则写入   直接输出流实例。

     

对于一次写入多少字节没有确切的指导。   虽然可以将所有数据写入流中   事件,这取决于外部因素,如行为   内核以及设备和套接字特性。最好的方法是   使用一些合理的缓冲区大小,例如512字节,1千字节(如   在上面的例子中),或页面大小(4千字节)。

因此,只要您为每个事件写入数据,就应该获得常规的NSStreamEventHasSpaceAvailable事件。