NSinputstream read data is returning null value? - ios

In my app I'm using NSStreams for client server communication. In the delegate method in event hasbytesAvailable when I'm reading the data its returning null
Case: when the length is 4096 then read is fails and returns nil; Means when the length is equal to buffer size its failing to read, even if I put the maxlength to 4000 and buffer size to 4096, then also whenever 4000 bytes are read its failing. what to do?
Here is the code:
case NSStreamEventHasBytesAvailable:
if (aStream == inputStream) {
uint8_t buffer[4096];
int len;
while ([inputStream hasBytesAvailable]) {
len = (int)[inputStream read:buffer maxLength:sizeof(buffer)];
NSLog(#"\nThe length is -- %d\n",len);
if (len > 0) {
NSData *data = [[NSData alloc] initWithBytes:buffer length:len];
output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
}
}
}
NSLog(#"\n\n%#\n\n",output);

Data read from a network connection will not always be received in the same sized chunks it was sent in. This means the receiver needs to:
Know exactly how much data to expect in a message.
Remember any "left over" data from the current message as that belongs to the next message.
One of the easiest ways of doing this properly is to prefix the message with a byte-count and then only attempt to read that much data from the network connection. That leaves any remaining data in the "network buffer" until the client wants it.
Your code is receiving a string, which will be NUL-terminated, so that means you need to read the data in fixed-sized chunks, check every byte until you find the end-of-string and then tack together the chunks before converting it to a string. You then need to remember any "left over" data for the next message. Complicated stuff, eh?
I'd go with the message size prefix, as that is what pretty much everyone else does.

I think the code is absolutely fine and it should read the data, may be after you've read 4096 bytes there might be some more bytes available and loop continues, and you are initialising the output variable again, So you might be missing it.
Use the following snippet:
if (aStream == inputStream) {
uint8_t buffer[4096];
int len;
NSString* tempOutput = #"";
while ([inputStream hasBytesAvailable]) {
len = (int)[inputStream read:buffer maxLength:sizeof(buffer)];
NSLog(#"\nThe length is -- %d\n",len);
if (len > 0) {
NSData *data = [[NSData alloc] initWithBytes:buffer length:len];
output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
tempOutput = [tempOutput stringByAppendingString:output];
// output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
}
}
output = tempOutput;
}

Related

How do I see the true contents of this string?

Please bear with me here since this question is not so easy to explain and word correctly.
I am using the following code in order to get data from a USB connected barcode reader, the scanner works fine and data is passed in as expected but my DB lookups fail and I believe they are failing because the data I am passing into the DBLookup method is incorrect but I am unable to see why, I think NSLog is helping me to show clear text data when in fact it isn't and I am stuck at debugging further.
Here is my code
- (void)didBarcodeDataReceive:(StarIoExtManager *)manager data:(NSData *)data {
NSLog(#"%s", __PRETTY_FUNCTION__);
NSMutableString *text = [NSMutableString stringWithString:#""];
const uint8_t *p = data.bytes;
for (int i = 0; i < data.length; i++) {
uint8_t ch = *(p + i);
[text appendFormat:#"%c", (char) ch];
}
NSLog(#"Scanned info as NSData was: %#", data); // raw NSData
//NSString *stringWithData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSString *stringWithData = [[NSString alloc] initWithBytes:(char *)data.bytes length:data.length encoding:NSUTF8StringEncoding];
NSLog(#"Scanned info as StringFromData was: %#", stringWithData);
NSLog(#"Scanned ch conversion is: %#", text);
int createTransactionResult = -1;
createTransactionResult = [NWBarCodeHelper createTransactionRowFromBarCode:text];
if ([NWTillHelper isDebug] == 1) {
NSLog(#"mPOP delegate holds barcode: %#", stringWithData);
if(createTransactionResult != 0) {
NSLog(#"TransactionListView:mPOPDelegate:createTransactionFrombarCode failed with errorCode %i", createTransactionResult);
}
}
}
My Debug outputs shows the correct data as follows
2017-04-19 10:19:01.868198 NWMobileTill[3751:1638657] Scanned info as NSData was: <30393235 38333834 33393439 35310d0a>
2017-04-19 10:19:01.868439 NWMobileTill[3751:1638657] Scanned info as StringFromData was: 09258384394951
2017-04-19 10:19:01.868652 NWMobileTill[3751:1638657] Scanned ch conversion is: 09258384394951
2017-04-19 10:19:01.868979 NWMobileTill[3751:1638657] NWBarCodeHelper:createTransactionRowFromBarcode:barcode = 09258384394951
2017-04-19 10:19:01.875938 NWMobileTill[3751:1638657] NWBarcodeHelper:CreateTransactionRowFromBarcode: 0 or more than one row returned, basic data error, item count = 0
But as you can see the last rows shows the DB lookup failing, I KNOW the method is correct cause when I scan using the iPhone camera and passing that data to the same method it works just fine on the same barcode so it must be something with the string that is passed in from the USB scanner that is tricking me out but I am unable to understand why and I think NSLog is trying to help me but not showing me the encoded data or something?
Your string contains a \r\n at the end. Have a look at the following code:
unsigned char bytes[] = {0x30, 0x39, 0x32, 0x35, 0x38, 0x33, 0x38, 0x34, 0x33, 0x39 ,0x34, 0x39, 0x35, 0x31, 0x0d, 0x0a};
NSData *data = [NSData dataWithBytes:bytes length:16];
NSString *stringWithData = [[NSString alloc] initWithBytes:(char *)data.bytes length:data.length encoding:NSUTF8StringEncoding];
NSLog(#"%#", stringWithData); // 09258384394951
NSLog(#"%lu", (unsigned long)[stringWithData length]); // 16
// remove \r\n at the end which gets added by the barcode scanner
NSString *string = [stringWithData stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#", string); // 09258384394951
NSLog(#"%lu", (unsigned long)[string length]); // 14
Or if you want to use your appendFormat approach you can just check if it is a valid digit before adding it to the string instead of removing it later.
To actually see the contents of your string you can either output the code point of each character in the string one by one or you can just set a breakpoint and Xcode will show it in the debugger:

Using base64 encoding with NSInputStream

I'm using RNCryptor to crypt files and for large files I use NSInputStream to separate file into chunks and to encode / decode those chunks. It worked great until I needed to base64 encode / decode chunks which are passed to streams.
Following code works perfectly fine for encoding, except it doesn't do base64 encoding:
NSInputStream *tmpStream = [[NSInputStream alloc] initWithURL:fileURL];
NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:filePath append:NO];
[tmpStream open];
[outputStream open];
__block NSMutableData *data = [NSMutableData dataWithLength:kBlockSize];
__block RNEncryptor *encryptor = nil;
dispatch_block_t readStreamBlock = ^{
[data setLength:kBlockSize];
NSInteger bytesRead = [tmpStream read:[data mutableBytes] maxLength:kBlockSize];
if (bytesRead < 0) {
NSLog(#"An error occurred while decrypting file stream");
if (resultBlock) { resultBlock(nil); }
return;
}
else if (bytesRead == 0){
[encryptor finish];
}
else {
[data setLength:bytesRead];
[encryptor addData:data];
}
};
encryptor = [[RNEncryptor alloc] initWithSettings:kRNCryptorAES256Settings
password:HUCryptorPassword
handler:^(RNCryptor *cryptor, NSData *data) {
[outputStream write:data.bytes maxLength:data.length];
if(cryptor.isFinished){
[outputStream close];
if (resultBlock) {
resultBlock([NSString stringWithFormat:#"%#", filePath]);
}
}
else {
readStreamBlock();
}
}];
readStreamBlock();
I tried adding base64 encoding after the data is encrypted and before writing to output stream:
NSData *base64Data = [data base64EncodedDataWithOptions:kNilOptions]; //Tried with all options
[outputStream write:base64Data.bytes maxLength:base64Data.length];
But that gives me incorrect results in output file.
Only correct way I get correctly encoded / decoded data is to load whole file in memory and call encode / decode methods.
NSData *resultData = [NSData dataWithContentsOfFile:filePath]; //This is location of output file stream
NSData *encodedData = [resultData base64EncodedDataWithOptions:kNilOptions]; //Gives me correct base64 encoded data
I want to avoid that since I need to make encoding / decoding big files, such as videos, sounds and images.
Is there any safe way to accomplish this?
4 base64 characters fit in three bytes.
When encoding make sure you encode chunks of 4 characters, holding over any remainder for the next portion of the stream. As the end of the stream encode what is left. Base64 pads the end of data that is not mod4, that is the "=" characters at the end.
For decoding decode in chunks that are mod3 encoded characters in length.
If you set it up so that you encode in 3 block (or multiples or 3) then the base64 encoding will also fall on a boundary.

Append bytes to NSInputStream to be read later sequentially

I am getting chunks of NSData sequentially from server, more than approx. 4096 bytes at a time, sequentially. Each received chunk may differ in its size.
What I would like to do, is to append all these bytes somewhere, and at the same time start reading from the beginning of the data, sequentially, 512 bytes at a time maximum.
While searching I've learned about using NSInputStream for this, and here is the code snippet:
uint8_t bytes[512];
UInt32 length;
NSInputStream *stream = [[NSInputStream alloc] initWithData:aData];
[stream open];
while (((length = [stream read:bytes maxLength:512]) > 0)) {
if ([self.inputStreamer isKindOfClass:[PLAudioInputStreamerNoOpenClose class]]) {
[self.inputStreamer hasData:bytes length:length];
}
}
While this just works, the initialized NSInputStream does not seem to allow appending additional bytes after it is initialized, so the only way I could think of is, to initialize NSInputStreams for every chunk of data, and block until it has reached its end, going on to do the same for next chunk of bytes, as the code above does.
Is there any more preferred solution for this kind of task? Any help will be appreciated. Thank you,
You need a 'read and write' stream. NSInputStream is read only and NSOutputStream is write only.
If I were you, I just use a NSMutableData and one int variables for 'current reading position'.
NSMutableData* myData = [[NSMutableData alloc] init];
NSInteger myPos = 0;
[myData appendData:..];
...
// need to check the range (myPos ~ [myData length])
NSData* nextBlockToRead = [NSData dataWithBytesNoCopy:((char*)[myData bytes] + myPos) length:512];
myPos += 512;

uint8_t conversion to NSString

I need to convert the contents of a single element in my uint8_t buffer to an NSString so that I can display it to a label on my iPhone app. I read in the contents of buffer OK from a TCP connection. I am not getting the proper encoding so that an element's value can be displayed correctly. For example, if buffer[4] has the contents of 0xFD or 253, how do I best get that transferred to the label? (The label is data1) Or is there a much simpler way? Thanks.
uint8_t buffer[64];
uint8_t tinybuffer[1];
int len;
// Read in contents from TCP connection, log dump, and display to label.
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
// Log dump
for(int i = 0; i < len; i++) {
NSLog(#"Returning byte %d : %x", i, buffer[i]);
}
NSLog(#"Finished Reading");
// Get data to the screen.
tinybuffer[0] = buffer[4];
NSString *str1 = [[NSString alloc] initWithBytes:tinybuffer length:1 encoding:NSUTF8StringEncoding];
_data1.text = str1;
NSString *str1 = [NSString stringWithFormat:#"%d", tinybuffer[0]];
should do what you want.

NSOutputStream not writing data properly

Using the NSStreamEventHasSpace available event, I am trying to write a simple NSString to to an NSOutputStream. Here is the contents of that event:
uint8_t *readBytes = (uint8_t *)[data mutableBytes];
readBytes += byteIndex; // instance variable to move pointer
int data_len = [data length];
unsigned int len = ((data_len - byteIndex >= 12) ?
12 : (data_len-byteIndex));
uint8_t buf[len];
(void)memcpy(buf, readBytes, len);
len = [output write:(const uint8_t *)buf maxLength:len];
NSLog(#"wrote: %s", buf);
byteIndex += len;
I pretty much took it right from Apple. The data is initialized in my viewDidLoad method with
data = [NSMutableData dataWithData:[#"test message" dataUsingEncoding:NSUTF8StringEncoding]];
[data retain];
The HasSpaceAvailable event is called twice. In the first one, the entire message is written with the characters "N." appended to it. In the second time, NSLog reports that a blank message was written (not null). Then, the EndEncountered event occurs. In that event, I have
NSLog(#"event: end encountered");
assert([stream isEqual:output]);
NSData *newData = [output propertyForKey: NSStreamDataWrittenToMemoryStreamKey];
if (!newData) {
NSLog(#"No data written to memory!");
} else {
NSLog(#"finished writing: %#", newData);
}
[stream close];
[stream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[stream release];
output = nil;
break;
I also got this from Apple. However, "No data written to memory!" is logged. No errors occur at anytime, and no data appears to have been received on the other end.
I seem to have fixed this by using low level Core Foundation methods instead of higher level NSStream methods. I used this article as a starting point:
http://oreilly.com/iphone/excerpts/iphone-sdk/network-programming.html
It covers input and output streams in great lenghts and has code examples.
Hope this helps.

Resources