Note that there are some explanatory texts on larger screens.

plurals
  1. PO"Connection reset by peer" errors with GCDAsyncUdpSocket on iOS6
    primarykey
    data
    text
    <p>I am having a problem with using GCDAsyncUdpSocket. I am using the iPad as a user interface app that interacts with another app - call it Host, the latter running on a separate Windows machine. Both machines are on their own private network, so they are on their own subnet. At certain points, the Host sends UDP packets to the iPad to instruct it which screen to show to the user, and the iPad sends user responses via UDP packets to the Host. Finally, the iPad periodically (at 2 Hz) sends simple "heartbeat" messages to the Host.</p> <p>This all works fine - for a while. Then, apparently, the iPad abruptly stops accepting the UDP packets from the Host - the latter experiences "Connection reset by peer" errors, while it (the iPad) is still successfully sending, and the Host receiving, the heartbeat messages.</p> <p>I'm thinking the problem comes from my confusion with respect to how Grand Central Dispatch (GCD) works. My iPad app is pretty simple; I based it off a tutorial on iOS programming (I'm a beginner here, but very experienced with Windows, Linux, embedded/real-time, and networking). It basically consists of a main screen, which creates a second screen from time to time. So the basic structure is this:</p> <ul> <li>main.m</li> <li>Delegate.m</li> <li>MainViewController.m</li> <li>PopupViewController.m</li> </ul> <p>The main.m and Delegate.m were created automagically by the Xcode during the tutorial, and have nothing special in them. The MainViewController.m is my "main screen", and owns the GCDAsyncUdpSocket that is used by the iPad app. The final file, PopupViewController.m, is the second screen, that is used like this:</p> <pre><code># MainViewController.m - (IBAction)sendResponseOne:(id)sender { // Send a message to Host [self sendUdpMessage:1]; // Switch to other view PopupViewController *vc = [[PopupViewController alloc] init]; [vc setMainScreen:self]; // Used to access UDP from 2nd screen [self presentViewController:vc animated:NO completion:nil]; } # PopupViewController.m - (IBAction)confirmAnswers:(id)sender { // Send a message to Host - calls same function as above main screen [self-&gt;mainScr sendUdpMessage:2]; [self dismissViewControllerAnimated:NO completion:nil]; } </code></pre> <p>Now for the code that sems to fail. First, here is the @interface section of MainViewController.m:</p> <pre><code># From MainViewController.m @interface MainViewController () { GCDAsyncUdpSocket *udpSocket; } @end </code></pre> <p>Here is how/where I create the UDP object:</p> <pre><code># From MainViewController.m - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { // Setup our socket, using the main dispatch queue udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; } return self; } </code></pre> <p>Here is where I bind to the port:</p> <pre><code># From MainViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Start UDP server int port = 12349; NSError *error = nil; if (![udpSocket bindToPort:port error:&amp;error]) { NSLog(@"Error starting server (bind): %@", error); return; } if (![udpSocket beginReceiving:&amp;error]) { [udpSocket close]; NSLog(@"Error starting server (recv): %@", error); return; } [self startPingTimer]; isRunning = YES; } </code></pre> <p>Here's the code that receives packets. Apparently, this function works fine for awhile, sometimes dozens of times, then unexpectedly fails.</p> <pre><code># From MainViewController.m - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext { if (data.length == sizeof(MyMessage)) { MyMessage msg; [data getBytes:&amp;msg length:sizeof(MyMessage)]; msg.magic = ntohl(msg.magic); msg.msgId = ntohl(msg.msgId); for (int i = 0; i &lt; 4; ++i) { msg.values[i] = ntohl(msg.values[i]); } if (msg.magic == 0xdeadcafe) { switch (msg.msgId) { case imiStateControl: self-&gt;iceState = (IceState)msg.values[0]; break; default: break; } } } } </code></pre> <p>I am at a loss as to why the didReceiveData function seems to work correctly for some random amount of time (and random number of messages sent/received). I wonder a couple of things:</p> <ol> <li><p>Is it valid for me to send a UDP message from the second screen? I think so, and sending never fails - it continues to work even after receiving fails.</p></li> <li><p>How does the didReceiveData get called anyway, and how could that get broken? If I was in Linux or an RTOS, I would probably have created an explicit thread that waits on packets; how does the GCD framework decide where a packet should go? </p></li> <li><p>Why would my app suddenly stop listening on the port? How do I detect/debug that?</p></li> <li><p>Does it matter that the GCDAsyncUdpSocket object is owned by the main screen, as opposed to the Delegate.m module?</p></li> <li><p>Is it appropriate to use the main dispatch queue, as I think I am doing? Indeed, am I doing that, and correctly?</p></li> </ol> <p>I'm totally at a loss, so of course, any advice would be greatly appreaciated! No need to answer all the questions - especially if your answer to one is the solution!</p> <p>Thanks!</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload