Slow file transfer using Multipeer Connectivity framework - ios7

I'm sending a file between two devices using iOS 7's Multipeer Connectivity Framework. I'm using NSStreams to tranfer the file, as my previous attempt using MCSession's sendData:toPeers:withMode was really unreliable. Unfortunately the transfer speeds I'm getting are really slow, around 100kb/s, which won't work for the application I'm working on. Here are my Input and Output stream delegate methods, which is where the file transfer happens.
Output Stream (in the delegate for the stream)
...//previous code
case NSStreamEventHasSpaceAvailable: {
//we will only open the stream when we want to send the file.
NSLog(#"Stream has space available");
//[self updateStatus:#"Sending"];
// If we don't have any data buffered, go read the next chunk of data.
if (totalBytesWritten< outgoingDataBuffer.length) {
//more stuff to read
int towrite;
int diff = outgoingDataBuffer.length-packetSize;
if (diff <= totalBytesWritten)
{
towrite = outgoingDataBuffer.length - totalBytesWritten;
} else
towrite = packetSize;
NSRange byteRange = {totalBytesWritten, towrite};
uint8_t buffer[towrite];
[outgoingDataBuffer getBytes:buffer range:byteRange];
NSInteger bytesWritten = [outputStream write:buffer maxLength:towrite];
totalBytesWritten += bytesWritten;
NSLog(#"Written %d out of %d bytes",totalBytesWritten, outgoingDataBuffer.length);
} else {
//we've written all we can write about the topic?
NSLog(#"Written %d out of %d bytes",totalBytesWritten, outgoingDataBuffer.length);
[self endStream];
}
// If we're not out of data completely, send the next chunk.
} break;
Input Stream
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
NSLog(#"Bytes Available");
//Sent when the input stream has bytes to read, we need to read bytes or else this wont be called again
//when this happens... we want to read as many bytes as we can
uint8_t buffer[1024];
int bytesRead;
bytesRead = [inputStream read:buffer maxLength:sizeof(buffer)];
[incomingDataBuffer appendBytes:&buffer length:bytesRead];
totalBytesRead += bytesRead;
NSLog(#"Read %d bytes, total read bytes: %d",bytesRead, totalBytesRead);
}break;
case NSStreamEventEndEncountered:
{
UIImage *newImage = [[UIImage alloc]initWithData:incomingDataBuffer];
[[self.detailViewController imageView] setImage:newImage];
NSLog(#"End Encountered");
[self closeStream];
//this should get called when there aren't any more bytes being sent down the stream
}
}
}
Is there a way to speed up this file transfer through either multithreading or using a slightly modified NSStream subclass that makes use of asynchronous sockets?

There are a couple of things I have noticed that cause slow file transfers:
Having a non-airdrop compatible device in the session (http://en.wikipedia.org/wiki/AirDrop)
WiFi issues (saturated network, router problems, the channel, etc.)
I have been getting decent speeds (and by decent I mean 500K / second) between an iPhone 5S, iPad Air and iPhone Simulator on the MAC.

I had some pretty decent transfer rates going for a while and suddenly it seemed that I was receiving one packet every 15 to 30 seconds. All looked fine on the sender side, but the packets trickle into the receiver. I use sendData:toPeers:withMode in my app.
One of my devices had WiFi turned off. Turning it back on restored my performance even though neither device is connected to a wifi network.
So I'm only ever doing peer-to-peer ad-hoc connections with MPC, but still, with no WiFi hub involved, it appears that iOS is making use of the WiFi signal to carry my data.) I did see in a presentation from Apple a while back that MPC over straight BlueTooth is a dog and I can tell you for a fact that it is.

MPC work on WiFi or bluetooth so it depends on what you are using. If you are using wifi to send files than you will get maximum speed as per the network strength.

Related

Mac equivalent to .Net SerialPort.ReadExisting()

I'm working on porting a VB program to the PC. It uses serial communication to interact with a physical device. I have a version up and running on the Mac using ORSSerialPort. However, once piece of the VB library that is great is the SerialPort.ReadExisting() function. This essentially reads any messages and discards them.
Has anyone built something similar on the Mac side? I've tried pulling out the ORSSerialPort into a function to directly read values (see below). However, unless I send a message I receive a null response. The readExisting function would be great for a scenario when things get a little out of alignment such as:
I send a message "Message1" to the device and nothing happens (was expecting Response1).
I send a message "Message2" to the device and receive: "Response1" instead of "Response2"
I'd like to detect this, call an equivalent to SerialPort.readExisting() since Response2 is the next to be found if I continue.
My read function:
-(NSString *) directRead
{
// Read Directly
int localPortFD = self.fileDescriptor;
struct timeval timeout;
int result=0;
fd_set localReadFDSet;
FD_ZERO(&localReadFDSet);
FD_SET(localPortFD, &localReadFDSet);
timeout.tv_sec = 0;
timeout.tv_usec = 100000; // Check to see if port closed every 100ms
result = select(localPortFD+1, &localReadFDSet, NULL, NULL, &timeout);
if (!self.isOpen) return nil; // Port closed while select call was waiting
if (result < 0)
{
NSLog(#"No Data To Read");
}
if (result == 0 || !FD_ISSET(localPortFD, &localReadFDSet)) return nil;
// Data is available
char buf[1024];
long lengthRead = read(localPortFD, buf, sizeof(buf));
if (lengthRead>0)
{
NSData *readData = [NSData dataWithBytes:buf length:lengthRead];
if (readData != nil)
return [[NSString alloc] initWithData:readData encoding:NSUTF8StringEncoding];
}
return nil;
}
You would think just doing:
NSString *result = nil;
do
{
result = [serialPort directRead];
NSLog(#"Past Message is: %#", result);
} while(result != nil);
Would flush out the messages. However, it acts as if there aren't any messages. However, if I call sendData:Message1 again I'd still see Response2 show up (in the above scenario).
Thanks for any and all help.
I'm having a little trouble following exactly what you're trying to do here, but in any case, it's something you'll have to implement at a higher level than ORSSerialPort or the POSIX serial read functions. ORSSerialPort will tell you any time bytes are available, as reported by the underlying standard POSIX file APIs it uses. So, there's nothing to "flush" from its perspective. If you get a response you were not expecting, e.g. a response to an "old" request, there's no way at all for the serial port to know that, it's up to your code to figure it out.
Something like this:
- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data
{
if (![self dataIsResponseToLatestRequest:data]) return; // Try again next time around
// Process a valid (expected response)
}
This raises a couple other issues. You can't be guaranteed that data will come in "whole packets". The serial hardware, low level serial APIs, and ORSSerialPort have no way of knowing what a whole packet looks like, and therefore just deliver data a bit at a time as it arrives. See this answer for more on that. So, it's up to you to buffer incoming data and assemble it into packets.
If you need to match requests you sent with responses coming in, you have to do that yourself. (sounds like you're really already doing this).
Lastly, if you're only receiving "Response1" after sending "Message2", this leads me to think there's a problem with the device on the other side of the connection not responding to requests properly. It would probably be worth fixing that, if you can (assuming it's your hardware/software).

malloc causing heap corruption

I'm learning how to communicate with USB devices with IOKit, and I wrote this piece of code:
// Global variable
char *dataBuffer;
- (void)startPolling {
if (!shouldPoll) { // Prevent polling twice
shouldPoll = YES;
timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(poll) userInfo:nil repeats:NO];
[self performSelectorInBackground:#selector(poll) withObject:nil];
}
}
- (void)poll {
dataBuffer = (char *)malloc(numBytes);
numBytes = 64;
returnCode = (*usbInterface)->ReadPipe(usbInterface, 2, dataBuffer, &numBytes);
// Handle received data in dataBuffer
free(dataBuffer);
[timer fire];
}
It works like this: another part of the code that works fine looks for the device, opens it, and then opens the correct interface. After that, when the user presses a button, it will call startPolling, which will set a timer that triggers the method poll every 0.5 seconds (sorta, the timer will fire again repeatedly).
In the poll method, the program will read the USB pipe and store data on dataBuffer. At first, I thought that I could alloc its memory once and reuse the pointer at every iteration, but for reasons I'm unfamiliar, the second ReadPipe call would fail. Always.
In an act of desperation, I came up with this (terrible?) idea: allocate and free the buffer memory at every iteration. For my surprise it did work, and I was able to read the device successfully.
The problem is that from time to time the program crashes with the error:
malloc: *** error for object 0x610000005890: Heap corruption detected, free list canary is damaged
*** set a breakpoint in malloc_error_break to debug
I really don't know what that means, let alone how to solve it. I set the buffer size to 64 to make sure that any data read would fit in the memory. Actual data is 18 bytes long.
Any clues?
These two statements should be the other way around:
dataBuffer = (char *)malloc(numBytes);
numBytes = 64;

High CPU consumption and latency while reading serial data

I have two functions in my software that cause important latency problems. The software is written in Objective-C. I receive serial data from an usb device and my goal is to encapsulate them and then to send them to another object which will process the data.
This section of the program causes large cpu and latency issues and I don't know how to solve this at all. The device only sends data when its state changes, thus when lots of changes occur well everything becomes laggy..
- (void)getSerialData {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self getSerialDataLoop];
});
}
- (void)getSerialDataLoop {
readThreadRunning = YES;
char byte_buffer[2]; // buffer for holding incoming data
int numBytes=0; // number of bytes read during read
NSString *text;
// this will loop untilthe serial port closes
while(TRUE) {
// read() blocks until some data is available or the port is closed
numBytes = (int)read(serialFileDescriptor, byte_buffer, 1); // read up to the size of the buffer
if(numBytes>0) {
///text = [NSString stringWithCString:byte_buffer encoding:NSSymbolStringEncoding];
if(![text isEqualToString:#""]){
text = [NSString stringWithUTF8String:byte_buffer];
[self performSelectorOnMainThread:#selector(processNSStringData:) withObject:text waitUntilDone:YES];
}
} else {
break; // Stop the thread if there is an error
}
}
// make sure the serial port is closed
if (serialFileDescriptor != -1) {
close(serialFileDescriptor);
serialFileDescriptor = -1;
}
// mark that the thread has quit
readThreadRunning = FALSE;
}
Do you have any ideas or pointers?
You've basically reinvented NSStream here. I would first recommend that you investigate this already available solution that ties into the run loop.
You also could easily be overwhelming yourself with calls to getSerialData. Nothing in your system prevents multiple calls to this routine, and if you make multiple calls, you'll get dueling concurrent operations. Using NSStream would address that. In any case, though, you shouldn't keep creating new read blocks if one is already running.
You're also reading one byte at time and processing it. This is likely your biggest impact. Calling back to the main thread for every byte is likely quite expensive. If nothing else you're creating a new NSString object for every byte.
Note that your code is very dangerous and could crash. You never initialize byte_buffer, and you only read one byte into it. When you call stringWithUTF8String:, you're assuming that the second byte is \0, which depends on the current state of the stack.

Data gets truncated when sending over NSStream

Ok so I have been at this bug all day, and I think I've got it narrowed down to the fundamental problem.
Background:
I am working on an app that has required me to write my own versions of NSNetService and NSNetServiceBrowser to allow for Bonjour over Bluetooth in iOS 5. It has been a great adventure, as I knew nothing of network programming before I started this project. I have learned a lot from various example projects and from the classic Unix Network Programming textbook. My implementation is based largely on Apple's DNSSDObjects sample project. I have added code to actually make the connection between devices once a service has been resolved. An NSInputStream and an NSOutputStream are attained with CFStreamCreatePairWithSocketToHost( ... ).
Problem:
I am trying to send some data over this connection. The data consists of an integer, a few NSStrings and an NSData object archived with NSKeyedArchiver. The size of the NSData is around 150kb so the size of the whole message is around 160kb. After sending the data over the connection I am getting the following exception when I try to unarchive...
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive
After further exploration I have noticed that the received data is only about 2kb.. The message is being truncated, thus rendering the archive "incomprehensible."
Potentially relevant code:
The method that sends the data to all connected devices
- (void) sendMessageToPeers:(MyMessage *)msg
{
NSEnumerator *e = [self.peers objectEnumerator];
//MyMessage conforms to NSCoding, messageAsData getter calls encodeWithCoder:
NSData *data = msg.messageAsData;
Peer *peer;
while (peer = [e nextObject]) {
if (![peer sendData:data]) {
NSLog(#"Could not send data to peer..");
}
}
}
The method in the Peer class that actually writes data to the NSOutputStream
- (BOOL) sendData:(NSData *)data
{
if (self.outputStream.hasSpaceAvailable) {
[self.outputStream write:data.bytes maxLength:data.length];
return YES;
}
else {
NSLog(#"PEER DIDN'T HAVE SPACE!!!");
return NO;
}
}
NSStreamDelegate method for handling stream events ("receiving" the data)
The buffer size in this code is 32768 b/c that's what was in whatever example code I learned from.. Is it arbitrary? I tried changing it to 200000, thinking that the problem was just that the buffer was too small, but it didn't change anything.. I don't think I fully understand what's happening.
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventHasBytesAvailable: {
NSInteger bytesRead;
uint8_t buffer[32768]; // is this the issue?
// uint8_t buffer[200000]; //this didn't change anything
bytesRead = [self.inputStream read:buffer maxLength:sizeof(buffer)];
if (bytesRead == -1) ...;
else if (bytesRead == 0) ...;
else {
NSData *data = [NSData dataWithBytes:buffer length:bytesRead];
[self didReceiveData:data];
}
} break;
/*omitted code for other events*/
}
}
NSStream over a network like that will be using a TCP connection. It can vary, but the maximum packet size is often around 2k. As the message you’re sending is actually 160k, it will be split up into multiple packets.
TCP abstracts this away to just be a stream of data, so you can be sure all these packets will receive in the correct order.
However, the stream:handleEvent: delegate method is probably being called when only the first 2k of data has arrived – there’s no way for it to know that there’s more coming until it does.
Note the method read:maxLength: doesn’t gauruntee you’ll always get that max length – in this case it seems to be only giving you up to 2k.
You should count up the actual bytesReceived, and concatenate all the data together until you receive the total amount you’re waiting for.
How does the receiver know how much data it wants? – you might want to design your protocol so before sending data, you send an integer of defined size indicating the length of the coming data. Alternatively, if you’re only ever sending one message over the socket, you could simply close it when finished, and have the receiver only unarchive after the socket is closed.
You seem to be reading from self.inputStream but the stream passed into your stream:handleEvent: method is called aStream. Are they referencing the same object somehow? Otherwise I'm not sure you're reading the stream that actually has bytes available

Immediate crash on device using SCNetworkReachabilityGetFlags with iOS app

I am using SCNetworkReachabilityCreateWithName & SCNetworkReachabilityGetFlags to test for network availability in my iOS device. When I run the app in the Simulator, or run it on the device, but attached to the debugger, everything's completely cool. However, when I run the app on the test device but detached from the debugger, I get a crash. [EDIT: just realized I didn't indicate where: in the call to SCNetworkReachabilityGetFlags] The odd thing is, it doesn't happen the first time I run the check. It happens on the second check. And using a try/catch does absolutely nothing.
The crash is immediate. There is no delay while the async polling occurs and the app just times out. It's immediate.
This is completely effed. I don't understand at all why this would be occurring. I am getting ready to go to a conference to exhibit the app and its accompanying web app. I was planning on walking the floor to show it off. But now I'm basically restricted to keeping my iPhone/iPod tethered to my computer in the booth.
Any ideas? Any work-arounds that don't keep me attached to the computer? And any long-term solutions that solve this? I use this method to check for network availability every time I perform any network activity. I am syncing my data to the Cloud, and run this check each time a sync occurs. I feel this is necessary to keep the user informed if there's a sudden break in the network connection.
Help????
Here's the code snippet.
+ (BOOL) networkConnected {
SCNetworkReachabilityFlags flags = 0;
SCNetworkReachabilityRef netReachability;
BOOL retrievedFlags = NO;
BOOL reachable = NO;
netReachability = SCNetworkReachabilityCreateWithName(CFAllocatorGetDefault(), [EXTERNAL_HOST UTF8String]);
#try {
if(netReachability) {
retrievedFlags = SCNetworkReachabilityGetFlags(netReachability, &flags);
CFRelease(netReachability);
}
if(!retrievedFlags || !flags)
reachable = NO;
else
reachable = YES;
} #catch (NSException *ex) {
reachable = NO;
} #finally {
return reachable;
}
}