我想连接pool.ntp.org进行时间同步。所以我正在创建一个套接字
sock=CFSocketCreate(NULL, PF_INET, SOCK_DGRAM, IPPROTO_UDP, kCFSocketDataCallBack|kCFSocketWriteCallBack|kCFSocketConnectCallBack, sockCallback, &sock_ctx);
然后我设置一个循环
sockref=CFSocketCreateRunLoopSource(NULL, sock, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), sockref, kCFRunLoopCommonModes);
并连接到地址
CFDataRef adrref=CFDataCreate(NULL, (const UInt8 *)&adr, sizeof(adr));
CFSocketError err=CFSocketConnectToAddress(sock, adrref,-1);
如果我有回调kCFSocketWriteCallBack,我会发送所需的数据
CFDataRef bufref=CFDataCreate(NULL, buffer, scl->NTP_PACKET_SIZE);
CFSocketError error = CFSocketSendData(scl->sock, NULL, bufref,3);
在这里之前一切都很完美。我的实际问题在
else if(callbackType==kCFSocketDataCallBack)
9/10次运行正常。服务器发送响应,我的过程继续。问题是,我在等待数据真正出现——继续我的应用程序逻辑。如果没有数据到来,CCD_ 1不会被触发,应用程序将永远等待。有没有办法让我在等待接收数据时超时?(无需自己使用NSTimer
重新连接到池)
我认为这里需要知道的重要一点是UDP本质上是不可靠的。
因此,数据包丢失,有时得不到响应,这是绝对可能的,也是正常的行为。你提到的10次中有9次是有效的,这对于基于UDP的协议来说听起来很好。
所以我认为你真的需要让你的代码更聪明一点。我还认为,使用计时器来确定你是否真的在一定时间内收到了回复是没有办法的。
幸运的是,在循环中安排CFRunLoopTimer
非常容易。你需要做的是:
- 当您第一次调用
CFSocketSendData
时,您还添加了一个CFRunLoopTimer
- 当您收到
kCFSocketDataCallBack
回调时,您将取消计时器 - 如果你从未收到回复,或者回复来得很晚,你的计时器就会启动。因此,您可以简单地从计时器回调中再次发送数据包,并使用
CFRunLoopTimerSetNextFireDate
再次调度它
您可以保留一个计数器,每次发送数据包时都会递增。然后你可以在一定的尝试之后放弃。
这是更多的代码,但它将使您的UDP应用程序更加可靠。