Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IOS receiving data timeout CFsocket

I want to connect with pool.ntp.org to time sync. So am creating a socket

    sock=CFSocketCreate(NULL, PF_INET, SOCK_DGRAM, IPPROTO_UDP, kCFSocketDataCallBack|kCFSocketWriteCallBack|kCFSocketConnectCallBack, sockCallback, &sock_ctx);

then am setting up a loop

    sockref=CFSocketCreateRunLoopSource(NULL, sock, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), sockref, kCFRunLoopCommonModes);

and connecting to the address

    CFDataRef adrref=CFDataCreate(NULL, (const UInt8 *)&adr, sizeof(adr));
    CFSocketError err=CFSocketConnectToAddress(sock, adrref,-1);

if i have callback kCFSocketWriteCallBack i send the required data

        CFDataRef bufref=CFDataCreate(NULL, buffer, scl->NTP_PACKET_SIZE);
    CFSocketError error = CFSocketSendData(scl->sock, NULL, bufref,3);

everything until here works perfect.My actual problem is at

else if(callbackType==kCFSocketDataCallBack)

9/10 times is working ok. server sending the response and my process continues. the problem is that am waiting for data to come to actual continue my app logic. if no data come kCFSocketDataCallBack is not triggered and app waiting for ever. Is there a way for me to put a timeout at waiting to receive data?( without having by myself a NSTimer to reconnect to the pool)

like image 799
tspentzas Avatar asked Nov 23 '12 09:11

tspentzas


1 Answers

I think the important thing to know here is that UDP is inherently unreliable.

It is therefore absolutely possible and normal behaviour that packets get lost and that you sometimes do not get a response. You mention 9 out of 10 times it works, that sounds pretty good for a UDP based protocol.

So I think you really need to make your code a little smarter. I also think there is no way around using a timer to find out if you actually received a response within a certain amount f time.

Fortunately it is very easy to schedule a CFRunLoopTimer on your loop. What you need to do is this:

  1. When you call CFSocketSendData for the first time you also add a CFRunLoopTimer.
  2. When you receive a kCFSocketDataCallBack callback you cancel the timer
  3. If you never receive a response, or if the response comes in really late, your timer will fire. So you can simply send the packet again from time timer callback and schedule it again with CFRunLoopTimerSetNextFireDate

You can keep a counter around that you increment every time you send a packet. Then you can give up after a certain amount of tries.

This is a little more code but it will make your UDP based app much more reliable.

like image 96
Stefan Arentz Avatar answered Sep 28 '22 12:09

Stefan Arentz