Question

I am trying to create a simple multiplayer turn-based game between iPhones. Right now all I want to do is pass in some string to my method, and have the method send the string through the NSOutputStream. I think I have properly connected my NSNetServices using NSNetServiceBrowser. Once they connect, my NSNetServiceDelegate has netService:didAcceptConnectionWithInputStream:outputStream: called, which should give me my i/o NSStream pair. My method looks like this:

-(void)netService:(NSNetService *)sender didAcceptConnectionWithInputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream{
    [self.myNet getInputStream:&inputStream outputStream:&outputStream];
    self.inStream = inputStream;
    self.outStream = outputStream;
    [self.inStream setDelegate:self];
    [self.inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [self.inStream open];
}

I think I have correctly set up NSInputStream. I also have a delegate for NSStream which is implementing stream:handleEvent:

It looks like this:

-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{
    NSInputStream *inStream = (NSInputStream *)aStream;
    BOOL shouldClose = NO;
    switch(eventCode) {
        case  NSStreamEventEndEncountered:
            shouldClose = YES;
            // If all data hasn't been read, fall through to the "has bytes" event
            if(![inStream hasBytesAvailable]) break;
        case NSStreamEventHasBytesAvailable: ; // We need a semicolon here before we can declare local variables
            uint8_t *buffer;
            NSUInteger length;
            BOOL freeBuffer = NO;
            // The stream has data. Try to get its internal buffer instead of creating one
            if(![inStream getBuffer:&buffer length:&length]) {
                // The stream couldn't provide its internal buffer. We have to make one ourselves
                buffer = malloc(BUFFER_LEN * sizeof(uint8_t));
                freeBuffer = YES;
                NSInteger result = [inStream read:buffer maxLength:BUFFER_LEN];
                if(result < 0) {
                    // error copying to buffer
                    break;
                }
                length = result;
            }
            // length bytes of data in buffer
            if(freeBuffer) free(buffer);
            break;
        case NSStreamEventErrorOccurred:
            // some other error
            shouldClose = YES;
            break;
        case NSStreamEventHasSpaceAvailable:
            break;
        case NSStreamEventNone:
            break;
        case NSStreamEventOpenCompleted:
            break;
    }
    if(shouldClose){
        [inStream close];
    }
}

I took that code from: this page. In that code, aStream should be self.inStream. I have looked at pages describing what to do for NSOutputStream, but none of them seem to be geared at a beginner like me. I have a few questions. Firstly, how do I set up a method that I pass the data in (NSData or maybe an NSString) and it sends it out through self.outStream. I would prefer an answer that explains the code, rather than just giving it to me. Secondly, should I open both of my streams in netService:didAcceptConnectionWithInputStream:outputStream:, and should I also scheduleInRunLoop the NSOutputStreamself.outStream`. Finally, am I doing everything wrong?

Thanks for your answers. Also this is my first question, so any constructive criticism is welcomed.

Was it helpful?

Solution

To write to output stream, check stream has space available and write data using

- (NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)length

So, code may be like following.

if ( self.outStream.hasSpaceAvailable ) [ self.outStream write:... maxLength: ];

If output stream doesn't have space available, later

-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode

method of the delegate of output stream would be called with eventCode = NSStreamEventHasSpaceAvailable.

You should open output stream and schedule it in netService:didAcceptConnectionWithInputStream:outputStream or when you receive NSStreamEventOpenCompleted of input stream. Both are OK.

If you don't want writing block thread, you need to prepare queue.

So code is like following

    NSMutableData*  uQueue;
    NSInputStream*  uIStream;
    NSOutputStream* uOStream;

    :
    :
    :

    uIStream.delegate = self;
    uOStream.delegate = self;

    [ uIStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode ];
    [ uOStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode ];

    [ uIStream open ];
    [ uOStream open ];

    uQueue = NSMutableData.data;


    :
    :
    :

-   (void)
Send
{   if ( uQueue.length )
    {   NSInteger wLength = [ uOStream write:(const uint8_t*)uQueue.bytes maxLength:uQueue.length ];
        if ( wLength > 0 ) [ uQueue replaceBytesInRange:NSMakeRange( 0, wLength ) withBytes:NULL length:0 ];
    }
}

-   (void)
Write:(NSData*)p
{   [ uQueue appendData:p ];
    if ( uOStream.hasSpaceAvailable ) [ self Send ];
}


-   (void)
stream:(NSStream*)pS
handleEvent:(NSStreamEvent)p
{   switch( p )
    {

    :
    :
    :

    case NSStreamEventHasSpaceAvailable:
        [ self Send ];
        break;
    }
}


Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top