iOS NSStream tcp server client can't communicate
Asked Answered
N

1

7

I have an issue with the development of a tcp server/client in objective c with Bonjour.

On the server side I open correctly the streams and I use the handleEvent function to send and receive data. But I don't know what is the proper way to send and receive data. I read this excellent post : Correct way to send data through a socket with NSOutputStream So I use a packet queued system to send data :

    switch(eventCode) {

    case NSStreamEventOpenCompleted: {
        NSLog(@"Complete");

    } break;

    case NSStreamEventHasSpaceAvailable: {
        if (stream == _outputStream)
            [self _sendData];
    } break;

...

- (void)_sendData {
flag_canSendDirectly = NO;
NSData *data = [_dataWriteQueue lastObject];
if (data == nil) {
    flag_canSendDirectly = YES;
    return;
}
uint8_t *readBytes = (uint8_t *)[data bytes];
readBytes += currentDataOffset;
NSUInteger dataLength = [data length];
NSUInteger lengthOfDataToWrite = (dataLength - currentDataOffset >= 1024) ? 1024 : (dataLength - currentDataOffset);
NSInteger bytesWritten = [_outputStream write:readBytes maxLength:lengthOfDataToWrite];
currentDataOffset += bytesWritten;
if (bytesWritten > 0) {
    self.currentDataOffset += bytesWritten;
    if (self.currentDataOffset == dataLength) {
        [self.dataWriteQueue removeLastObject];
        self.currentDataOffset = 0;
    }
}

}

. So basically i just send separate my data on many packets. But I don't see how to reconstruct the data on the client side. And I think i didn't correctly understood the NSStreamEventHasBytesAvailable event. Because here I send many packets but this event on the client side is call only once. And when I reconstruct the data from the buffer the data appears to be corrupted. I would really appreciate if someone can clarify all this, the documentation is not really clear on this point (or maybe I miss a point ..) .

        case NSStreamEventHasBytesAvailable: {
        if (stream == _inputStream)
        {
            //read data
            uint8_t buffer[1024];
            int len;
            while ([_inputStream hasBytesAvailable])
            {
                len = [_inputStream read:buffer maxLength:sizeof(buffer)];
                if (len > 0)
                {
                    NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
                    //NSData *theData = [[NSData alloc] initWithBytes:buffer length:len];
                    UnitySendMessage("AppleBonjour_UnityNetworkManager(Clone)", "OnLocalClientReceiveMessageFromServer", [output cStringUsingEncoding:NSUTF8StringEncoding]);
                }
            }
        }
Nuke answered 13/11, 2015 at 13:40 Comment(4)
Have you tried a simple test? Send a single short string. What do you get in your variable "output" when you do this? fwiw; there doesn't have to be a single call of bytes available for each send, they will be combined if they arrive in time and one read will get them all. Please try this test and tell us what you get.Helterskelter
Oh, I forgot one more thing; you can test the receiver with a simple tcp program like telnet. Just connect to your port and type some text. If the receiver code is working you should get your text directly.Helterskelter
Okay a simple string worked. So if i send a string bigger than the buffer, I just have to reconstruct it each time the NSStreamEventHasBytesAvailable is called. But how can i have a notification when the string has been sent completely ?Nuke
You cannot. A stream is a stream. You have to encode the end of message in the stream itself. When the receiver processes incoming bytes, it has to look for the 'end of message marker'. Depending on the sender and since the receiver can be busy you have to be ready to process a block which might have an end of message in and more bytes as well, the start of the next message.Helterskelter
T
3

It seems, that you use two variables currentDataOffset -- an instance variable and a property -- for the same purpose. If you indeed need both of them, you should set 0 to currentDataOffset as well. But I think the fragment should look like:

readBytes += currentDataOffset;
NSUInteger dataLength = [data length];
NSUInteger lengthOfDataToWrite = MIN(dataLength - currentDataOffset, 1024);
NSInteger bytesWritten = [_outputStream write:readBytes maxLength:lengthOfDataToWrite];
if (bytesWritten > 0) {
    currentDataOffset += bytesWritten;
    if (currentDataOffset == dataLength) {
        [self.dataWriteQueue removeLastObject];
        currentDataOffset = 0;
    }
}
Tumble answered 22/11, 2015 at 11:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.