3

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.

Community
  • 1
  • 1
multitaskPro
  • 569
  • 4
  • 14

1 Answers1

-1

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


Satachito
  • 5,838
  • 36
  • 43
  • Thanks for this. However, I am not entirely sure of the purpose of `Write:` It seems to do the same thing as `stream:handleEvent:` Also, would I need to have an if statement checking if the stream that I am handling in `stream:handleEvent:` is an `NSInputStream` or an `NSOutputStream`? – multitaskPro Apr 20 '14 at 20:26
  • Use Write: to send data. Do you wanna use stream:handleEvent: to send data? – Satachito Apr 20 '14 at 20:38
  • If data has 10Kbytes and if your output stream can send only 5K bytes per one call, the remained 5K data will be hold in uQueue and will be sent when you receive NSStreamEventHasSpaceAvailable. – Satachito Apr 20 '14 at 20:40
  • You may need to check your stream if your delegate of the input stream and output stream are same. – Satachito Apr 20 '14 at 20:42
  • iam sending images, which splits into different data blocks, but how to know on the receivers end, that a whole image was transferred and it can use the data to create it? – Peter Lapisu Dec 03 '14 at 18:01