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 に答える