在VC++中实现同步Internet时间
写作目的:(此段可跳过)
同步Internet时间,即通过Internet的校时网站传来的数据校准本机时间。但是现在网络上查到的相关编程资料并不多,且其中多是VB和Delphi的代码,VC的代码我还没找到过。是这个东西太难了?应该不是;是太简单了?那也总该有人写吧。
我认为,自己懂和让别人懂压根不是一回事,我写这篇文章,目的当然是后者。当然,理工科出身的河蚌不大可能像文科出身的河蚌那样修出光彩夺目的珍珠来,所以,行文有不妥之处,欢迎指正。
校时原理:
互联网上有很多时间服务器能够提供准确的时间,我们通过连接到这样的服务器来获取时间值。这里向大家介绍一下服务器传来的数据格式先。数据一共四个字节(4 Byte),我们可以在接收数据后对它进行“重新组装”,把组装所得的值放在一个32位的整数里,这个值的意义是:自1900年1月1日0时0分0秒 至 服务器发送这个时间数据时 所经历的秒数。显然,任何一个时刻到1900年所经历的秒数是唯一的,因此,由服务器传来的时间数据即可推出现在的时间,然后用API函数调整系统的时间即可。
流程图如下:
设计目标:
好了,我们的目标是:(没有蛀牙~)
-_-!!
常言说一图千言,我们还是看图吧:
程序的实现:
从技术角度来看,解决三个问题即可:
1. 通过网络通信从服务器获取时间数据。
2. 处理基于1900年的时间数据,转化为我们常见的时间形式。
3. 解决网络造成的延时问题。
下面分条讲述:
1. 通过网络通信从服务器获取时间数据。
至于接收数据,没什么可说的,这里用CSocket就可以了。
代码片断:
CSocket sockClient;
sockClient.Create(); //创建socket
//for debug
m_info += "Connect server: " + strServer + " ";
UpdateData(FALSE);
//for debug
sockClient.Connect((LPCTSTR)strServer, 37); // strServer:时间服务器网址; 37:端口号
DWORD dwTime = 0; //用来存放服务器传来的标准时间数据
unsigned char nTime[8]; //临时接收数据
memset(nTime, 0, sizeof(nTime));
sockClient.Receive(nTime, sizeof(nTime)); //接收服务器发送来得4个字节的数据
sockClient.Close(); //关闭socket
//for debug
m_info += "Connect shut down. ";
UpdateData(FALSE);
//for debug
dwTime += nTime[0] << 24; //整合数据
dwTime += nTime[1] << 16;
dwTime += nTime[2] << 8;
dwTime += nTime[3];
if(0 == dwTime) return FALSE;
到此为止,服务器传来的时间数据经过“重新组装”已经正确放置到DWORD类型的变量 dwTime 里面了。下面我们接着对其进行必要的处理。
2. 处理基于1900年的时间数据,转化为我们常见的时间形式。
在前面我们提到,时间数据已经正确放置到变量 dwTime 里面了。那么,怎样由它得到现在的时间呢?
微软已经给我们提供了一个很好用的时间类:CTime。不过,MFC的CTime类的时间起点是基于1970年的,而dwTime 里面的秒数是从1900年计时的。
用CTime?无法由 dwTime 中的数据直接构造CTime类的对象。
用C的函数库?我尝试了多次,N次碰壁。
说起最终敲定的实现方法,其实很简单- 改变计时基准。
时间转换的方法如下:
1. 用 COleDateTime 和 COleDateTimeSpan 算出1900年1月1日0时0分0秒 到 1970年1月1日0时0分0秒 所经历的秒数 dwSec00to70。
2. 从 dwTime 中减去 dwSec00to70。此后,dwTime 所代表的就是自1970年1月1日0时0分0秒以来逝去的秒数――显然,dwTime 已经被我们转变为基于1970年的时间值了,这回可以用CTime进行处理了。
怎么样?不复杂吧。(想起了近几天屡试屡败的经历和查阅的N多资料,自己吐血先)
代码片断:
//服务器传来的数据是自从1900年以来的秒数
//取得 1900~1970 的时间差(以秒数计算) ,放在dwSpan里面
COleDateTime t00( 1900, 1, 1, 0, 0, 0 ); // 1900.1.1 00:00:00
COleDateTime t70( 1970, 1, 1, 0, 0, 0 ); // 1970.1.1 00:00:00
COleDateTimeSpan ts70to00 = t70 - t00;
DWORD dwSpan = (DWORD)ts70to00.GetTotalSeconds();
ASSERT( dwSpan == 2208988800L );
//把时间变为基于1970年的,便于用CTime处理
dwTime -= dwSpan;
//考虑网络延迟因素
dwTime += dwDely;
//构造当前时间的CTime对象
CTime timeNow = (CTime)dwTime;
//for debug
m_info += timeNow.Format("%Y.%m.%d %H:%M:%S ");
UpdateData(FALSE);
//for debug
3. 解决网络造成的延时问题。
在从服务器获取时间数据时,由于网络本身的不稳定性,一般会有时间上的延迟(几秒以内),这样一来,从服务器接收到的数据总早于的真实时间。解决的办法是设定一个计时器,计算出本机从开始网络连接到接收完数据所耗费的时间dwDelay,然后加到 dwTime 上进行补偿。这样一来误差就可以控制在1秒以内(如果你不用你的爱机控制导弹飞行或者航天发射,应该够用了),详见源码。