Выпуск в письменной форме `NSOutputStream`. - PullRequest
2 голосов
/ 12 августа 2011

У меня есть один базовый вопрос: работая с 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

Теперь, когда приложению нужно что-то отправить через сокет (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 Ответ

8 голосов
/ 12 августа 2011

Если вы не дождетесь события, вызов записи будет заблокирован, пока не освободится место.Обычно вы хотите, чтобы ваш код работал асинхронно, поэтому наилучшим решением является ожидание NSStreamEventHasSpaceAvailable.

Что касается получения уведомления о доступном пространстве, см. Документацию здесь :

Если делегат получает событие NSStreamEventHasSpaceAvailable и ничего не записывает в поток, он не получает дальнейшие доступные для пространства события из цикла выполнения до тех пор, пока объект NSOutputStream не получит больше байтов.Когда это происходит, цикл запуска перезапускается для доступных для пространства событий.Если этот сценарий вероятен в вашей реализации, вы можете сделать так, чтобы делегат установил флаг, когда он не записывает в поток при получении события NSStreamEventHasSpaceAvailable.Позже, когда ваша программа имеет больше байтов для записи, она может проверить этот флаг и, если установлен, записать непосредственно в экземпляр потока вывода.

Не существует четкого руководства относительно количества байтов, которые нужно записать за один раз.,Хотя может быть возможно записать все данные в поток за одно событие, это зависит от внешних факторов, таких как поведение ядра и характеристики устройства и сокета.Наилучшим подходом является использование некоторого разумного размера буфера, такого как 512 байт, один килобайт (как в примере выше) или размер страницы (четыре килобайта).

Так что вы должны получать регулярныеСобытия NSStreamEventHasSpaceAvailable, если вы записываете данные для каждого события.

...