程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 利用RFC868協議編寫網絡對時程序

利用RFC868協議編寫網絡對時程序

編輯:關於VC++

一、網絡授時服務

網絡授時服務是在網絡上設置一些時間服務器,用戶通過Internet訪問這些時間服務器就可同步本地計算機時鐘的服務。網絡授時服務有三個協議,分別是Network Time Protocol (RFC-1305),Daytime Protocol (RFC-867),Time Protocol (RFC-868)。

有關這些協議的詳細信息,可參考以下網站:

http://www.boulder.nist.gov/timefreq/service/its.htm
http://www.faqs.org/rfcs/rfc867.html
http://www.faqs.org/rfcs/rfc1305.html
http://www.faqs.org/rfcs/rfc868.html

我的程序中列出的時間服務器列表,主要來自:

http://www.boulder.nist.gov/timefreq/service/time-servers.html

更多的時間服務器列表請參考以下網站:

http://www.eecis.udel.edu/~mills/ntp/servers.html

二、Time Protocol (RFC-868)協議

Time Protocol (RFC-868)協議是一種較簡單的協議。此協議提供了一個獨立於站點的,機器可讀的日期和時間信息。時間服務返回的是以秒數,是從1900年1月1日午夜到現在的秒數。

這個協議可以工作在TCP和UDP協議下。下面是通過TCP協議工作的時間協議的工作過程:這裡S代表服務器,C代表客戶。

S: 檢測端口37
U: 連接到端口37
S: 以32位二進制數發送時間
U: 接收時間
U: 關閉連接
S: 關閉連接

如果服務器不能決定現在是什麼時間,服務器會拒絕連接或不發送任何數據而直接關閉連接。

下面我們看看使用UDP協議的情況:這裡S代表服務器,C代表客戶。

S: 檢測端口37
U: 發送一個空數據報到端口37
S: 接收這個空數據報
S: 發送包含32位二進制數(用於表示時間)的數據報
U: 接收時間數據報

如果服務器不能決定現在是什麼時間,服務器會拋棄接收到的數據報而不作出任何應答。

三、網絡對時的程序實現

下面是使用TCP協議的實現網絡對時的部分代碼。GetRemoteTime 函數主要通過連接服務器szSever,並取得其回傳的32位值:

BOOL GetRemoteTime(char* szSever, unsigned long& ulTime)
{
  SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);  //使用UDP協議
  if(sock == INVALID_SOCKET)
  {
    return FALSE;
  }
  sockaddr_in severAddr;
  severAddr.sin_family = AF_INET;
  severAddr.sin_port = htons(NET_TIME_PORT);
  severAddr.sin_addr.S_un.S_addr = inet_addr(szSever);
  if (sendto(sock, (char*)&ulTime, 4, 0, (sockaddr*)&severAddr, sizeof(severAddr)) == 4)
  {
    unsigned long flag = 1;
    if ((ioctlsocket(sock, FIONBIO, &flag) == 0))
    {
      struct fd_set mask;
      FD_ZERO(&mask);
      FD_SET(sock, &mask);

      struct timeval timeout;
      timeout.tv_sec = TIMEOUT_RECEIVE;
      timeout.tv_usec = 0;

      if (select(0, &mask, NULL, NULL, &timeout) == 1)
      {
        if (recv(sock, (char*)&ulTime, 4, 0) == 4)
        {
          ulTime = ntohl(ulTime);
          closesocket(sock);
          return TRUE;
        }
      }
    }
  }
  closesocket(sock);
  return FALSE;
}

MySetTime 函數的功能是將32位值轉換為系統時間,並設置系統時間。

void MySetTime(unsigned long ulTime)
{
  FILETIME ft;
  SYSTEMTIME st;

  st.wYear = 1900;
  st.wMonth = 1;
  st.wDay = 1;
  st.wHour = 0;
  st.wMinute = 0;
  st.wSecond = 0;
  st.wMilliseconds = 0;

  SystemTimeToFileTime(&st, &ft);
  LARGE_INTEGER li = *(LARGE_INTEGER*)&ft;
  li.QuadPart += (LONGLONG)10000000 * ulTime;
  ft = *(FILETIME*)&li;
  FileTimeToSystemTime(&ft, &st);
  SetSystemTime(&st);
}
int APIENTRY WinMain(HINSTANCE hInstance,
     HINSTANCE hPrevInstance,
     LPSTR   lpCmdLine,
     int    nCmdShow)
{
  //初始化TCP協議
  WSADATA wsaData;
  if(WSAStartup(MAKEWORD(2,2), &wsaData)!= 0)
  {
    MessageBox(NULL, "初始化網絡協議失敗!", "錯誤報告", MB_OK|MB_ICONHAND);
    return -1;
  }

  int i = 0;
  unsigned long ulTime = 0;

  while (sever[i] != NULL)
  {
    if (GetRemoteTime(sever[i], ulTime))
    {
      MySetTime(ulTime);
      char buff[100];
      sprintf(buff, "已成功與時間服務器\r\n%s\r\n的時間同步", sever[i]);
      MessageBox(NULL, buff, "成功報告", MB_OK|MB_ICONINFORMATION);
      return 0;
    }
    i++;
  }
  MessageBox(NULL, "所有服務器均不能正常連接或超時!", "錯誤報告", MB_OK|MB_ICONHAND);
  WSACleanup();
  return 0;
}

至於使用UDP協議實現程序詳見本文附帶的代碼。 

四、結束語

程序在VC6+WinXP下編寫調試正確,並在Win98下運行正確。時間精度本人不敢妄下結論,但經與電視台對時,應小於1秒。也可到國家授時中心上去對時。但通常第一次打開這個網頁時服務器時間和本地時間差別大些,多刷新幾次又幾乎一致了。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved