轉自:http://blog.csdn.net/xiaoxian8023/article/details/7250385
在做YH維護的時候,偶爾會碰到這樣的問題:電腦的非正常關機導致系統時間出錯(變為了2002-1-1),從而影響到項目系統的使用。尤其是設計到money的系統,如果時間錯誤,可能會導致無法想象的后果。所以我們可能需要用系統和網絡的雙重驗證。
通過收集、修改、優化和測試,剔除了一些錯誤的和速度超慢的,只剩下了4種可行的方案。這些方案中主要有3類:
一、通過向某網站發送請求,獲取服務器響應請求的時間
二、獲某時間網頁的html或xml碼,讀取其中的時間。
三、通過向某授時服務器發送請求,獲取網絡時間
我把這些方法封裝到了一個類庫里了。
下面是具體的類:NetTime類(包含了多種獲取網絡時間的方法,標有速度),需要添加引用:COM-Microsoft Xml 3.0
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using MSXML2;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Text.RegularExpressions;
- using System.IO.Compression;
- using System.Xml;
- namespace WebTime
- {
- /// <summary>
- /// 網絡時間
- /// </summary>
- public class NetTime
- {
- #region 獲取標准北京時間1 速度100ms
- /// <summary>
- /// [1].獲取標准北京時間1,讀取http://www.beijing-time.org/time.asp
- /// </summary>
- /// <returns></returns>
- public DateTime GetBeijingTime()
- {
- #region 格式
- // t0=new Date().getTime(); nyear=2012; nmonth=2; nday=11;
- // nwday=6; nhrs=0; nmin=23; nsec=0;
- #endregion
- DateTime dt;
- WebRequest wrt = null;
- WebResponse wrp = null;
- try
- {
- wrt = WebRequest.Create("http://www.beijing-time.org/time.asp");
- wrp = wrt.GetResponse();
- string html = string.Empty;
- using (Stream stream = wrp.GetResponseStream())
- {
- using (StreamReader sr = new StreamReader(stream, Encoding.UTF8))
- {
- html = sr.ReadToEnd();
- }
- }
- string[] tempArray = html.Split(';');
- for (int i = 0; i < tempArray.Length; i++)
- {
- tempArray[i] = tempArray[i].Replace("\r\n", "");
- }
- string year = tempArray[1].Split('=')[1];
- string month = tempArray[2].Split('=')[1];
- string day = tempArray[3].Split('=')[1];
- string hour = tempArray[5].Split('=')[1];
- string minite = tempArray[6].Split('=')[1];
- string second = tempArray[7].Split('=')[1];
- //for (int i = 0; i < tempArray.Length; i++)
- //{
- // tempArray[i] = tempArray[i].Replace("\r\n", "");
- //}
- //string year = tempArray[1].Substring(tempArray[1].IndexOf("nyear=") + 6);
- //string month = tempArray[2].Substring(tempArray[2].IndexOf("nmonth=") + 7);
- //string day = tempArray[3].Substring(tempArray[3].IndexOf("nday=") + 5);
- //string hour = tempArray[5].Substring(tempArray[5].IndexOf("nhrs=") + 5);
- //string minite = tempArray[6].Substring(tempArray[6].IndexOf("nmin=") + 5);
- //string second = tempArray[7].Substring(tempArray[7].IndexOf("nsec=") + 5);
- dt = DateTime.Parse(year + "-" + month + "-" + day + " " + hour + ":" + minite + ":" + second);
- }
- catch (WebException)
- {
- return DateTime.Parse("2011-1-1");
- }
- catch (Exception)
- {
- return DateTime.Parse("2011-1-1");
- }
- finally
- {
- if (wrp != null)
- wrp.Close();
- if (wrt != null)
- wrt.Abort();
- }
- return dt;
- }
- #endregion
- #region 獲取網站響應請求的時間,速度200ms
- /// <summary>
- /// [2]獲取網站響應請求的時間,速度200ms
- /// </summary>
- /// <param name="hUrl">網址</param>
- /// <returns>DateTime</returns>
- /// <remarks></remarks>
- public DateTime GetNetTime( string hUrl)
- {
- string datetxt = null; //請求回應的時間
- string[] date1 = null; //分割后的星期和日期
- string date2 = ""; //分割后的日期和GMT
- string[] date3 = null; //最終成型的日期
- DateTime nTime =DateTime.Today ;
- string localtime = "";
- string mon = "";
- XMLHTTP objHttp = new XMLHTTP();
- objHttp.open("GET", hUrl, false);
- try
- {
- objHttp.send();
- //獲取網站回應請求的日期時間。如: Wed, 08 Feb 2012 06:34:58 GMT
- datetxt = objHttp.getResponseHeader("Date");
- //分割時間
- date1 = datetxt.Split(',');
- }
- catch (Exception ex)
- {
- throw ex;
- }
- //
- if (date1 ==null )
- {
- localtime = "網絡驗證失敗,請重新啟動或檢查網絡設置";
- }
- else if (date1.Length < 1)
- {
- localtime = "網絡驗證失敗,請重新啟動或檢查網絡設置";
- }
- else
- {
- //將時間中的GMT去掉
- date2 = date1[1].Replace("GMT", "");
- //如: 08 Feb 2012 06:34:58 GMT
- date3 = date2.Split(' ');
- //如: 08 Feb 2012 06:34:58
- switch (date3[2])
- {
- case "Jan":
- mon = "01";
- break;
- case "Feb":
- mon = "02";
- break;
- case "Mar":
- mon = "03";
- break;
- case "Apr":
- mon = "04";
- break;
- case "May":
- mon = "05";
- break;
- case "Jun":
- mon = "06";
- break;
- case "Jul":
- mon = "07";
- break;
- case "Aug":
- mon = "08";
- break;
- case "Sep":
- mon = "09";
- break;
- case "Oct":
- mon = "10";
- break;
- case "Nov":
- mon = "11";
- break;
- case "Dec":
- mon = "12";
- break;
- }
- //最終反饋是日期和時間
- localtime = date3[3] + "/" + mon + "/" + date3[1] + " " + date3[4];
- //獲取的協調世界時
- DateTime sTime = Convert.ToDateTime(localtime);
- //轉換為當前計算機所處時區的時間,即東八區時間
- nTime = TimeZone.CurrentTimeZone.ToLocalTime(sTime);
- }
- objHttp = null;
- return nTime;
- }
- #endregion
- #region 獲取標准北京時間3,速度500ms-1500ms
- /// <summary>
- /// [3]獲取標准北京時間2 ,讀取(xml)http://www.time.ac.cn/timeflash.asp?user=flash
- /// </summary>
- /// <returns></returns>
- public DateTime GetStandardTime()
- {
- #region 文件格式
- /// //<?xml version="1.0" encoding="GB2312" ?>
- //- <ntsc>
- //- <time>
- // <year>2011</year>
- // <month>7</month>
- // <day>10</day>
- // <Weekday />
- // <hour>19</hour>
- // <minite>45</minite>
- // <second>37</second>
- // <Millisecond />
- // </time>
- // </ntsc>
- #endregion
- DateTime dt;
- WebRequest wrt = null;
- WebResponse wrp = null;
- try
- {
- wrt = WebRequest.Create("http://www.time.ac.cn/timeflash.asp?user=flash");
- wrt.Credentials = CredentialCache.DefaultCredentials;
- wrp = wrt.GetResponse();
- StreamReader sr = new StreamReader(wrp.GetResponseStream(), Encoding.UTF8);
- string html = sr.ReadToEnd();
- sr.Close();
- wrp.Close();
- //int yearIndex = html.IndexOf("<year>") ;
- //int secondIndex = html.IndexOf("</second>");
- //html = html.Substring(yearIndex, secondIndex - yearIndex);
- html = html.Substring(51, 109);
- string[] s1 = html.Split(new char[2] { '<', '>' });
- string year = s1[2];
- string month = s1[6];
- string day = s1[10];
- string hour = s1[18];
- string minite = s1[22];
- string second = s1[26];
- dt = DateTime.Parse(year + "-" + month + "-" + day + " " + hour + ":" + minite + ":" + second);
- }
- catch (WebException)
- {
- return DateTime.Parse("0001-1-1");
- }
- catch (Exception)
- {
- return DateTime.Parse("0001-1-1");
- }
- finally
- {
- if (wrp != null)
- wrp.Close();
- if (wrt != null)
- wrt.Abort();
- }
- return dt;
- }
- #endregion
- #region 訪問標准校時服務器端口獲取網絡時間 速度:1000-2000ms
- /// <summary>
- /// 獲取網絡時間,通過標准校時服務器
- /// </summary>
- /// <param name="HostName">主機名</param>
- /// <param name="PortNum">端口號</param>
- /// <returns>DateTime</returns>
- public DateTime GetInternetTime(string HostName, int PortNum)
- {
- DateTime official, localtime;
- string returndata = null;
- string[] dates = new string[4];
- string[] times = new string[4];
- string[] tokens = new string[11];
- TcpClient tcpclient = new TcpClient();
- try
- {
- tcpclient.Connect(HostName, PortNum);
- NetworkStream networkStream = tcpclient.GetStream();
- if (networkStream.CanWrite && networkStream.CanRead)
- {
- Byte[] sendBytes = Encoding.ASCII.GetBytes("Hello");
- networkStream.Write(sendBytes, 0, sendBytes.Length);
- byte[] bytes = new byte[tcpclient.ReceiveBufferSize];
- networkStream.Read(bytes, 0, (int)tcpclient.ReceiveBufferSize);
- returndata = Encoding.ASCII.GetString(bytes);
- }
- tcpclient.Close();
- }
- catch (Exception excep)
- {
- throw excep;
- }
- tokens = returndata.Split(' ');
- dates = tokens[1].Split('-');
- times = tokens[2].Split(':');
- official = new DateTime(Int32.Parse(dates[0]) + 2000, Int32.Parse(dates[1]), Int32.Parse(dates[2]),
- Int32.Parse(times[0]), Int32.Parse(times[1]), Int32.Parse(times[2]));
- localtime = TimeZone.CurrentTimeZone.ToLocalTime(official);
- return localtime;
- }
- #endregion
- }
- }
先說一下NetTime類,4個方法,
第一個方法GetBeijingTime()速度最快,100ms的反應時間,快得沒話說,不過在跟其他校時網比較時間時,它的時間比別的校時網站時間要提前10s。
第二個方法GetNetTime(string Url),反應時間取決於你訪問的網頁,我這里用的是百度。通過查資料,百度的平均加載速度為:0.48s(2007年),而Google的加載速度為:0.48s(2007年0.87s)。這2個都可以,當然也可以用網易163或者其他的。推薦用這個。我用的Google,反應時間為200ms左右。完全可以滿足你的一般需求。
第三個方法GetStandardTime(),網站的加載速度為0.55s。但是處理起來有點費時,反應時間在500-1500ms。
第四個方法GetInternetTime(string HostName,int PortNum),同樣取決於授時服務器,我這里用的是time-a.timefreq.bldrdoc.gov,端口號為13,反應時間在1000-2000ms。
網絡時間應用舉例:
- private void timer2_Tick(object sender, EventArgs e)
- {
- //速度100ms
- try
- {
- NetTime t = new NetTime();
- string time = t.GetBeijingTime().ToString();
- lblNetTime.Text = time;
- }
- catch (Exception ex)
- {
- lblNetTime.Text = ex.Message;
- }
- }
- private void timer3_Tick(object sender, EventArgs e)
- {
- //速度200ms
- try
- {
- NetTime t = new NetTime();
- string time = t.GetNetTime("http://www.google.com.hk/").ToString();
- label3.Text = time;
- }
- catch (Exception ex)
- {
- label3.Text = ex.Message ;
- }
- }
- private void timer4_Tick(object sender, EventArgs e)
- {
- //速度500-1500ms
- try
- {
- NetTime t = new NetTime();
- string time = t.GetStandardTime().ToString();
- label4.Text = time;
- }
- catch (Exception ex)
- {
- label4.Text = ex.Message;
- }
- }
- private void timer5_Tick(object sender, EventArgs e)
- {
- try
- {
- NetTime t = new NetTime();
- string time = t.GetInternetTime("time-a.timefreq.bldrdoc.gov",13).ToString();
- label5.Text = time;
- }
- catch (Exception ex)
- {
- label5.Text = ex.Message;
- }
- }
前面說系統和網絡的雙重驗證,其實只要判斷一下2者 在日期上不同,就用網絡時間來糾正一下本地時間即可。
下面是系統時間類
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Runtime.InteropServices;
- namespace WebTime
- {
- /// <summary>
- /// 系統時間結構
- /// </summary>
- public struct SystemTime
- {
- public ushort wyear; //年
- public ushort wmonth; //月
- public ushort wdayofweek; //星期
- public ushort wday; //日
- public ushort whour; //時
- public ushort wminute; //分
- public ushort wsecond; //秒
- public ushort wmilliseconds; //毫秒
- /// <summary>
- /// 從System.DateTime轉換。
- /// </summary>
- /// <param name="time">System.DateTime類型的時間。</param>
- public void fromDateTime(DateTime time)
- {
- wyear = (ushort)time.Year;
- wmonth = (ushort)time.Month;
- wdayofweek = (ushort)time.DayOfWeek;
- wday = (ushort)time.Day;
- whour = (ushort)time.Hour;
- wminute = (ushort)time.Minute;
- wsecond = (ushort)time.Second;
- wmilliseconds = (ushort)time.Millisecond;
- }
- /// <summary>
- /// 轉換為system.DateTime類型。
- /// </summary>
- /// <returns></returns>
- public DateTime toDateTime()
- {
- return new DateTime(wyear, wmonth, wday, whour, wminute, wsecond, wmilliseconds);
- }
- /// <summary>
- /// 靜態方法。轉換為system.DateTime類型。
- /// </summary>
- /// <param name="time">systemtime類型的時間。</param>
- /// <returns></returns>
- public static DateTime toDateTime(SystemTime time)
- {
- return time.toDateTime();
- }
- }
- /// <summary>
- /// 系統時間類
- /// </summary>
- public class SysTime
- {
- [DllImport("Kernel32.dll ")]
- public static extern bool SetSystemTime(ref SystemTime SystemTime);
- [DllImport("Kernel32.dll ")]
- public static extern void GetSystemTime(ref SystemTime SystemTime);
- }
- }
其中定義一個系統時間的結構體類型和一個類。SetSystemTime是來設置系統時間,GetSystemTime用來獲取系統時間。
下面是用網絡時間來校對系統時間的源碼:
- private void btnCorretTime_Click(object sender, EventArgs e)
- {
- NetTime nt = new NetTime();
- //獲取網絡時間
- string time = nt.GetNetTime("http://www.google.com.hk/").ToString();
- DateTime t=Convert.ToDateTime(time);
- //實例化一個系統時間結構體對象
- SystemTime st = new SystemTime();
- //將當前計算機所在時區時間轉化為協調世界時
- t = TimeZone.CurrentTimeZone.ToUniversalTime(t);
- st.fromDateTime(t);
- //修改系統時間
- SysTime.SetSystemTime(ref st);
- MessageBox.Show("時間改為"+ st.toDateTime().ToString());
- }
所以只需要給項目系統添加一個通過網絡時間來校對系統時間功能,而其他代碼不用改,還是用系統時間,這樣也符合了開閉原則。
轉載的朋友請說明出處:http://blog.csdn.net/xiaoxian8023/article/details/7250385