unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls,winsock ;// 添加winsock單元,直接調用WINSOCK API; type TForm1 = class(TForm) Button1: TButton; Label1: TLabel; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private procedure MyConnect(host:string){ Private declarations }; Procedure MySend( s:string); function MyReceive: string; procedure MyDisconnect; function MySyncTime(host:string):TDateTime; public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} type // NTP 數據格式 tNTPGram = packed record head1, head2, file://其中,head1爲LI VN 及Mode(見圖二); head3, head4 : byte; RootDelay : longint; RootDisperson : longint; RefID : longint; Ref1, Ref2, Org1, Org2, Rcv1, Rcv2, Xmit1, Xmit2 : longint;//Transmit Timestamp(傳輸時間戳) end; |
// 用於轉換本機網絡字節順序; lr = packed record l1, l2, l3, l4 : byte; end; var MySocket:Tsocket; fiMaxSockets:integer; MyAddr: TSockAddrIn; UDPSize:integer; const Port=123;// SNTP端口號必須爲123; procedure TForm1.Button1Click(Sender: TObject); begin label1.Caption:= timetostr(MySyncTime('bernina.ethz.ch')); end; procedure TForm1.FormCreate(Sender: TObject); file://初始化套接字; var sData:TWSAData; fsStackDescription:string; begin if WSAStartup($101, sData) = SOCKET_ERROR then raise Exception.Create('Winsock Initialization Error.'); fsStackDescription := StrPas(sData.szDescription); UDPSize := sData.iMaxUdpDg; fiMaxSockets := sData.iMaxSockets; MySocket:= INVALID_SOCKET ; end; |
procedure TForm1.MyConnect(host:string);//創建套接字,域名解析; var fsPeerAddress:string; function ResolveHost(const psHost: string; var psIP: string): u_long;//主機名解析成IP地址; var pa: PChar; sa: TInAddr; aHost: PHostEnt; begin psIP := psHost; if CompareText(psHost, 'LOCALHOST') = 0 then begin sa.S_un_b.s_b1 := #127; sa.S_un_b.s_b2 := #0; sa.S_un_b.s_b3 := #0; sa.S_un_b.s_b4 := #1; psIP := '127.0.0.1'; Result := sa.s_addr; end else begin Result := inet_addr(PChar(psHost)); if Result = u_long(INADDR_NONE) then begin aHost := GetHostByName(PChar(psHost)); pa := aHost^.h_addr_list^; sa.S_un_b.s_b1 := pa[0]; sa.S_un_b.s_b2 := pa[1]; sa.S_un_b.s_b3 := pa[2]; sa.S_un_b.s_b4 := pa[3]; psIP := IntToStr(Ord(sa.S_un_b.s_b1)) + '.' + IntToStr(Ord(sa.S_un_b.s_b2)) + '.' + IntToStr(Ord(sa.S_un_b.s_b3)) + '.' + IntToStr(Ord(sa.S_un_b.s_b4)); Result := sa.s_addr; end; end; end; begin MySocket:=socket(PF_INET,SOCK_DGRAM, IPPROTO_IP);//創建套接字,採用UDP/IP協議; if MySocket = INVALID_SOCKET then begin raise Exception.Create('套接字創建失敗!'); end; try with MyAddr do begin file://時間服務器名字; sin_family := PF_INET; sin_port := HToNS(Port); sin_addr.S_addr := ResolveHost(host, fsPeerAddress); end; except On E: Exception do begin if MySocket <> INVALID_SOCKET then begin CloseSocket(MySocket); end;; raise; end; end; end; |
procedure TForm1.MySend( s:string); file://發送 請求時間數據報; begin SendTo(MySocket, s[1], Length(s), 0,Myaddr , sizeof(Myaddr)); end; function TForm1.MyReceive; file://接收服務器時間數據報; var AddrVoid: TSockAddrIn; fsUDPBuffer:string; i:integer; begin SetLength(fsUDPBuffer, UDPSize); i:= SizeOf(AddrVoid) ; result := Copy(fsUDPBuffer,1,Recvfrom(Mysocket, fsUDPBuffer[1], Length(fsUDPBuffer), 0, AddrVoid , i) ); end; function flip(var n : longint) : longint; file://網絡字節順序與本機字節順序轉換; var n1, n2 : lr; begin n1 := lr(n); n2.l1 := n1.l4; n2.l2 := n1.l3; n2.l3 := n1.l2; n2.l4 := n1.l1; flip := longint(n2); end; function tzbias : double; // 獲取本地時間區與UTC時間誤差; var tz : TTimeZoneInformation; begin GetTimeZoneInformation(tz); result := tz.Bias / 1440; end; const maxint2 = 4294967296.0; |
// 將DELPHI的 TDateTime 格式轉換成爲 NTP 時間戳(timestamp)格式 ; procedure dt2ntp(dt : tdatetime; var nsec, nfrac : longint); var d, d1 : double; begin d := dt + tzbias - 2; d := d * 86400; d1 := d; if d1 > maxint then begin d1 := d1 - maxint2; end; nsec := trunc(d1); d1 := ((frac(d) * 1000) / 1000) * maxint2; if d1 > maxint then begin d1 := d1 - maxint2; end; nfrac := trunc(d1); end; |
// 將NTP 時間戳(timestamp)格式轉換成爲DELPHI的 TDateTime 格式; function ntp2dt(nsec, nfrac : longint) : tdatetime; var d, d1 : double; begin d := nsec; if d < 0 then d := maxint2 + d - 1; d1 := nfrac; if d1 < 0 then d1 := maxint2 + d1 - 1; d1 := d1 / maxint2; d1 := trunc(d1 * 1000) / 1000; result := (d + d1) / 86400; result := result - tzbias + 2; end; |
function TForm1.MySyncTime(host:string):TDateTime;//獲取時間服務器上的標準時間,同時同步本地時間; var ng : TNTPGram; s : string; SysTimeVar : TSystemTime; begin fillchar(ng, sizeof(ng), 0); file://將 SNTP數據報初始化; ng.head1 := $1B; // 設置SNTP數據報頭爲SNTP 協議版本3,模式3(客戶機),即二進制00011011; dt2ntp(now, ng.Xmit1, ng.xmit2);//將本機時間轉換爲數據報時間格式; ng.Xmit1 := flip(ng.xmit1); ng.Xmit2 := flip(ng.xmit2); setlength(s, sizeof(ng)); move(ng, s[1], sizeof(ng)); try MyConnect(host); MySend(s); s := MyReceive; move(s[1], ng, sizeof(ng)); result := ntp2dt(flip(ng.xmit1), flip(ng.xmit2));// 將收到的數據報時間格式轉換爲本機時間; DateTimeToSystemTime( result, SysTimeVar) ; SetLocalTime( SysTimeVar ); file://同步本地時間; MyDisconnect; except MyDisconnect; showmessage('時間同步失敗!'); application.Terminate; end; end; |
procedure TForm1.MyDisconnect; file://關閉套接字; begin if MySocket <> INVALID_SOCKET then begin CloseSocket(MySocket); end; end; end. |
augean.eleceng.adelaide.edu.au*bernina.ethz.chbiofiz.mf.uni-lj.si*black-ice.cc.vt.educhime.utoronto.ca*churchy.udel.edu (128.4.1.5) clepsydra.dec.comclock.psu.educlock.tricity.wsu.edu (192.31.216.30) constellation.ecn.uoknor.educuckoo.nevada.edu*delphi.cs.ucla.edudominator.eecs.harvard.edu |