8

我想与 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 次工作正常。服务器发送响应,我的过程继续。问题是我正在等待数据来实际继续我的应用程序逻辑。如果没有数据来kCFSocketDataCallBack不被触发并且应用程序永远等待。有没有办法让我在等待接收数据时设置超时?(无需自己NSTimer重新连接到池)

4

1 回答 1

1

我认为这里要知道的重要一点是UDP本质上是不可靠的。

因此,数据包丢失并且您有时没有得到响应是绝对可能且正常的行为。您提到 10 次中有 9 次有效,这对于基于 UDP 的协议来说听起来相当不错。

所以我认为你真的需要让你的代码更聪明一点。我还认为没有办法使用计时器来确定您是否在一定时间内确实收到了回复。

CFRunLoopTimer幸运的是,在您的循环中安排一个非常容易。你需要做的是:

  1. 当您CFSocketSendData第一次调用时,您还会添加一个CFRunLoopTimer.
  2. 当您收到kCFSocketDataCallBack回调时,您会取消计时器
  3. 如果您从未收到响应,或者响应来得太晚,您的计时器将触发。因此,您可以简单地从时间计时器回调中再次发送数据包并再次安排它CFRunLoopTimerSetNextFireDate

您可以保留一个计数器,每次发送数据包时都会增加该计数器。然后你可以在尝试一定次数后放弃。

这是一个多一点的代码,但它会让你的基于 UDP 的应用程序更加可靠。

于 2013-10-22T00:49:52.497 回答