Very high UDP packet loss on iOS (GCDAsyncUdpSockets)

I am working on an iPhone application to control physical hardware.
The procedure is as follows:

  • application
  • sends a specific 8-byte "wake up" datagram on the broadcast channel, on port 8089; the message is resent.
  • external equipment that listens to port 8089, receives the message and sends a 94-byte datagram containing, among other things, the IP address and MAC address of the hardware; this also applies to the broadcast channel. application
  • stops sending the wake-up message, stores the IP address, and starts communicating with the equipment over the TCP socket

The procedure usually works. However, I often get unexplained UDP packet receive loss; that is, I am sending an 8 byte wake up signal, but I am not receiving a 94 byte response. When the application works, it works great: I hardly ever lose one packet, and if the application misses the first 94-byte message, it gets the second or third. When it is down, it skips all packets, all the time. The “non-working” stage can last minutes or hours; I did not find any obvious trigger - as if at some stage, for no reason, the trick stops working.

I did some very extensive debugging before asking the question. I have monitored sockets through rvictl and tcpdump and verified that my logs reflect what is happening at the socket level. To take external hardware out of the equation, I created a mirrored application that behaves like hardware. I tried changing ports, I tried to close and nil sockets all the time, until resetting them to pause and restart receiving. None of this worked.

I have developed my communication library with GCDAsyncUdpSocket. To be on the safe side, I experimented with the non-GCD version as well; The result is the same. I've built my own minimal wrapper around C sockets; I have the same behavior.

Here is my implementation code:

GCDAsyncUdpSocket *udpSocket;

- (void)startUdpBroadcast {

    if (udpSocket == nil)
    {
        // Setup our socket.
        udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        [udpSocket setIPv6Enabled:NO];
    }

    NSLog(@"Listening for a message on %@",[self getBroadcastAddress]);

    NSError *bindError = nil;
    NSError *enableError = nil;
    NSError *receivingError = nil;

    [udpSocket bindToPort:8089 error:&bindError];
    if (bindError) {
        NSLog(@"Error, can't bind: %@",[bindError localizedDescription]);
        return;
    }
    [udpSocket enableBroadcast:YES error:&enableError];
    if (enableError) {
        NSLog(@"Error, can't enable broadcast: %@",[enableError localizedDescription]);
        return;
    }

    if (![udpSocket beginReceiving:&receivingError])
    {
        NSLog(@"Error, can't receive: %@",[receivingError localizedDescription]);
        return;
    }

    [self logInfo:@"Start listening to UDP boradcast channel"];

    [self sendUdpSignal];

    repeatedBroadcastTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendUdpSignal) userInfo:nil repeats:YES];

}

-(void)sendUdpSignal {
    NSData* signal = [self udpBroadcastSignal] ;
    NSLog(@"Send broadcast signal %@ on %@",signal,  [self currentNetworkSsid]);
    [udpSocket sendData:signal toHost:[self getBroadcastAddress] port:8089 withTimeout:10 tag:0];
}

      

Do you have any suggestions? Is there a way to make sure that socket reception never stops?

Thanks in advance,
Davide

+3


source to share





All Articles