2 votes

Problème d'écriture de `NSOutputStream`.

J'ai une question fondamentale, En travaillant avec NSOutputStream devons-nous attendre NSStreamEventHasSpaceAvailable pour envoyer le paquet, donc nous pouvons appeler , [NSOutputStream write] au fur et à mesure de ses besoins,

Je crois NSStream devrait s'occuper de la fonction d'écriture...

Si ce n'est pas correct, veuillez donner votre avis sur la logique suivante,

\===== Pour écrire sur NSOutputStream ================= Avoir une file d'attente pour ajouter les paquets à envoyer. // 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

et sa mise en œuvre

#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

Maintenant, lorsque l'application a quelque chose à envoyer via socket( NSStream ), il doit l'ajouter à la file d'attente,

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

et ce bout de code quand on obtient 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 ;
    }
}

Je m'attendais à ce que l'application continue à recevoir l'événement, chaque fois que OutputStream envoie des données, mais je ne les ai reçues qu'une seule fois... :( S'il vous plaît aider ...

8voto

Mike Weller Points 28387

Si vous n'attendez pas l'événement, l'appel en écriture se bloquera jusqu'à ce qu'un espace soit disponible. En règle générale, il est préférable de concevoir votre code de manière à ce qu'il fonctionne de manière asychrone. Attendre l'événement NSStreamEventHasSpaceAvailable est donc la meilleure solution.

Quant au moment où vous recevez la notification d'espace disponible, voir la documentation ici :

Si le délégué reçoit un événement NSStreamEventHasSpaceAvailable et que et qu'il n'écrit rien dans le flux, il ne reçoit plus d'événements événements de disponibilité d'espace de la boucle d'exécution jusqu'à ce que l'objet NSOutputStream reçoive plus d'octets. Lorsque cela se produit, la boucle d'exécution est redémarre pour les événements disponibles dans l'espace. Si ce scénario est probable dans votre mise en œuvre, vous pouvez demander au délégué de définir un indicateur lorsqu'il n'écrit pas dans le flux à la réception d'un événement NSStreamEventHasSpaceAvailable. Plus tard, lorsque votre programme aura plus d'octets à écrire, il peut vérifier cet indicateur et, s'il est activé, écrire dans le instance de output-stream directement.

Il n'existe pas de directive ferme sur le nombre d'octets à écrire en une seule fois. Bien qu'il soit possible d'écrire toutes les données dans le flux en une seule fois, cela dépend des conditions externes. événement, cela dépend de facteurs externes, tels que le comportement du noyau et les caractéristiques des périphériques et des sockets. La meilleure approche consiste à d'utiliser une taille de tampon raisonnable, comme 512 octets, un kilooctet (comme dans l'exemple dans l'exemple ci-dessus), ou une taille de page (quatre kilo-octets).

Vous devriez donc recevoir régulièrement des événements NSStreamEventHasSpaceAvailable tant que vous écrivez des données pour chaque événement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X