Delphi开发环境中应用层网络协议的实现
生活随笔
收集整理的這篇文章主要介紹了
Delphi开发环境中应用层网络协议的实现
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
已經(jīng)進(jìn)入Internet網(wǎng)絡(luò)時(shí)代了,許多新出的軟件都擁有網(wǎng)絡(luò)功能。其實(shí),在這些軟件背后所依靠的技術(shù)基礎(chǔ)就是一系列的Inernet網(wǎng)絡(luò)協(xié)議標(biāo)準(zhǔn),亦即TCP/IP系列協(xié)議。
下面本人簡要介紹一下在Delphi環(huán)境下,直接采用winsock套接字編程,應(yīng)用SNTP協(xié)議開發(fā)出具有網(wǎng)絡(luò)時(shí)間校準(zhǔn)功能的應(yīng)用,以此來說明如何在編程實(shí)踐中實(shí)現(xiàn)應(yīng)用層網(wǎng)絡(luò)協(xié)議,相信感興趣的讀者能從中舉一反三。?
一、程序原理:
1、 SNTP協(xié)議的運(yùn)作機(jī)制 SNTP(簡單網(wǎng)絡(luò)時(shí)間協(xié)議)是在UDP協(xié)議基礎(chǔ)上發(fā)展出來的應(yīng)用協(xié)議,目前廣泛應(yīng)用于整個(gè)INTERNET上計(jì)算機(jī)時(shí)鐘的同步,依據(jù)同步源的性能及網(wǎng)絡(luò)路徑的差異,提供1~50ms的較準(zhǔn)精度(資料來源:RFC-2030)。
SNTP報(bào)文封裝見圖三,有關(guān)IP報(bào)頭和UDP報(bào)頭的詳細(xì)結(jié)構(gòu)這里不再贅述,需要知道的是針對我們的SNTP應(yīng)用,UDP報(bào)頭的源端口和目的端口值均應(yīng)設(shè)定為123,SNTP數(shù)據(jù)緊跟在UDP報(bào)頭后(數(shù)據(jù)格式見圖二),SNTP協(xié)議應(yīng)用分服務(wù)器端和客戶端,根據(jù)我們的應(yīng)用需求只要考慮SNTP客戶端單播模式(UNICAST MODE)的運(yùn)作:在該模式下,客戶端初始化NTP數(shù)據(jù)報(bào),將SNTP數(shù)據(jù)頭的VN(協(xié)議版本號)值設(shè)置成3(即Version3);Mode值設(shè)置成3(即客戶端模式);數(shù)據(jù)區(qū)的Transmit Timestamp(傳輸時(shí)間戳)值設(shè)置成客戶機(jī)當(dāng)前時(shí)間;然后向一個(gè)特定的單播模式時(shí)間服務(wù)器發(fā)出該數(shù)據(jù)報(bào)并接受服務(wù)器的回答,收到回答報(bào)文后從其中的傳輸時(shí)間戳里獲取同步時(shí)間。需要注意的是請求數(shù)據(jù)報(bào)初始化時(shí)其傳輸時(shí)間戳必須被設(shè)定為客戶機(jī)當(dāng)前時(shí)間,這樣可以通過計(jì)算,消除服務(wù)器客戶機(jī)之間的傳播延遲,有效地將同步精度控制在10ms級范圍內(nèi)。
2、用WINSOCK API實(shí)現(xiàn)SNTP 首先,調(diào)用socket(domain,type,protocol)建立套接字,其中domain 為 PF_INET(Internet域);type 為SOCK_DGRAM(UDP數(shù)據(jù)報(bào)); protpcol為IPPROTO_IP(IP協(xié)議)。其次,調(diào)用sendto(socket_id,buf,buflen,flags,to,tolen)發(fā)送SNTP客戶端請求,其中,buf為SNTP數(shù)據(jù);to為時(shí)間服務(wù)器名字,它是一結(jié)構(gòu),包含域名、端口號、32位主機(jī)地址等;socket_id為套接字號 。接著,調(diào)用recvfrom (socket_id,buf,buflen,flags,from,fromlen)接收SNTP數(shù)據(jù)回答,其中buf為SNTP數(shù)據(jù)回答。最后,將回答數(shù)據(jù)中的傳輸時(shí)間戳(Transmit Timestamp)轉(zhuǎn)換成Delphi的時(shí)間格式,并調(diào)用WINDOWS API函數(shù)SetLocalTime同步本地計(jì)算機(jī)時(shí)間,結(jié)束時(shí)關(guān)閉套接字。
二、編程步驟:
在IDE中建立一個(gè)新工程,缺省的Form上放一個(gè)label控件、一個(gè)button控件,在button控件的OnClick事件句柄中鍵入執(zhí)行同步時(shí)間函數(shù)及l(fā)abel控件時(shí)間顯示代碼,另外在單元文件中建立MySyncTime等5個(gè)有關(guān)時(shí)間同步函數(shù)(過程),最后,編譯該工程。
三、完整的單元文件如下:
?
?
程序在Delphi3+ win98se 上通過。
附部分SMTP時(shí)間服務(wù)器網(wǎng)址:
下面本人簡要介紹一下在Delphi環(huán)境下,直接采用winsock套接字編程,應(yīng)用SNTP協(xié)議開發(fā)出具有網(wǎng)絡(luò)時(shí)間校準(zhǔn)功能的應(yīng)用,以此來說明如何在編程實(shí)踐中實(shí)現(xiàn)應(yīng)用層網(wǎng)絡(luò)協(xié)議,相信感興趣的讀者能從中舉一反三。?
一、程序原理:
1、 SNTP協(xié)議的運(yùn)作機(jī)制 SNTP(簡單網(wǎng)絡(luò)時(shí)間協(xié)議)是在UDP協(xié)議基礎(chǔ)上發(fā)展出來的應(yīng)用協(xié)議,目前廣泛應(yīng)用于整個(gè)INTERNET上計(jì)算機(jī)時(shí)鐘的同步,依據(jù)同步源的性能及網(wǎng)絡(luò)路徑的差異,提供1~50ms的較準(zhǔn)精度(資料來源:RFC-2030)。
SNTP報(bào)文封裝見圖三,有關(guān)IP報(bào)頭和UDP報(bào)頭的詳細(xì)結(jié)構(gòu)這里不再贅述,需要知道的是針對我們的SNTP應(yīng)用,UDP報(bào)頭的源端口和目的端口值均應(yīng)設(shè)定為123,SNTP數(shù)據(jù)緊跟在UDP報(bào)頭后(數(shù)據(jù)格式見圖二),SNTP協(xié)議應(yīng)用分服務(wù)器端和客戶端,根據(jù)我們的應(yīng)用需求只要考慮SNTP客戶端單播模式(UNICAST MODE)的運(yùn)作:在該模式下,客戶端初始化NTP數(shù)據(jù)報(bào),將SNTP數(shù)據(jù)頭的VN(協(xié)議版本號)值設(shè)置成3(即Version3);Mode值設(shè)置成3(即客戶端模式);數(shù)據(jù)區(qū)的Transmit Timestamp(傳輸時(shí)間戳)值設(shè)置成客戶機(jī)當(dāng)前時(shí)間;然后向一個(gè)特定的單播模式時(shí)間服務(wù)器發(fā)出該數(shù)據(jù)報(bào)并接受服務(wù)器的回答,收到回答報(bào)文后從其中的傳輸時(shí)間戳里獲取同步時(shí)間。需要注意的是請求數(shù)據(jù)報(bào)初始化時(shí)其傳輸時(shí)間戳必須被設(shè)定為客戶機(jī)當(dāng)前時(shí)間,這樣可以通過計(jì)算,消除服務(wù)器客戶機(jī)之間的傳播延遲,有效地將同步精度控制在10ms級范圍內(nèi)。
2、用WINSOCK API實(shí)現(xiàn)SNTP 首先,調(diào)用socket(domain,type,protocol)建立套接字,其中domain 為 PF_INET(Internet域);type 為SOCK_DGRAM(UDP數(shù)據(jù)報(bào)); protpcol為IPPROTO_IP(IP協(xié)議)。其次,調(diào)用sendto(socket_id,buf,buflen,flags,to,tolen)發(fā)送SNTP客戶端請求,其中,buf為SNTP數(shù)據(jù);to為時(shí)間服務(wù)器名字,它是一結(jié)構(gòu),包含域名、端口號、32位主機(jī)地址等;socket_id為套接字號 。接著,調(diào)用recvfrom (socket_id,buf,buflen,flags,from,fromlen)接收SNTP數(shù)據(jù)回答,其中buf為SNTP數(shù)據(jù)回答。最后,將回答數(shù)據(jù)中的傳輸時(shí)間戳(Transmit Timestamp)轉(zhuǎn)換成Delphi的時(shí)間格式,并調(diào)用WINDOWS API函數(shù)SetLocalTime同步本地計(jì)算機(jī)時(shí)間,結(jié)束時(shí)關(guān)閉套接字。
二、編程步驟:
在IDE中建立一個(gè)新工程,缺省的Form上放一個(gè)label控件、一個(gè)button控件,在button控件的OnClick事件句柄中鍵入執(zhí)行同步時(shí)間函數(shù)及l(fā)abel控件時(shí)間顯示代碼,另外在單元文件中建立MySyncTime等5個(gè)有關(guān)時(shí)間同步函數(shù)(過程),最后,編譯該工程。
三、完整的單元文件如下:
unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls,winsock ;// 添加winsock單元,直接調(diào)用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 數(shù)據(jù)格式 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(傳輸時(shí)間戳) end; |
// 用于轉(zhuǎn)換本機(jī)網(wǎng)絡(luò)字節(jié)順序; 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;//主機(jī)名解析成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協(xié)議; if MySocket = INVALID_SOCKET then begin raise Exception.Create('套接字建立失敗!'); end; try with MyAddr do begin file://時(shí)間服務(wù)器名字; 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://發(fā)送 請求時(shí)間數(shù)據(jù)報(bào); begin SendTo(MySocket, s[1], Length(s), 0,Myaddr , sizeof(Myaddr)); end; function TForm1.MyReceive; file://接收服務(wù)器時(shí)間數(shù)據(jù)報(bào); 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://網(wǎng)絡(luò)字節(jié)順序與本機(jī)字節(jié)順序轉(zhuǎn)換; 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; // 獲取本地時(shí)間區(qū)與UTC時(shí)間偏差; var tz : TTimeZoneInformation; begin GetTimeZoneInformation(tz); result := tz.Bias / 1440; end; const maxint2 = 4294967296.0; |
?
// 將DELPHI的 TDateTime 格式轉(zhuǎn)換成為 NTP 時(shí)間戳(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 時(shí)間戳(timestamp)格式轉(zhuǎn)換成為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;//獲取時(shí)間服務(wù)器上的標(biāo)準(zhǔn)時(shí)間,同時(shí)同步本地時(shí)間; var ng : TNTPGram; s : string; SysTimeVar : TSystemTime; begin fillchar(ng, sizeof(ng), 0); file://將 SNTP數(shù)據(jù)報(bào)初始化; ng.head1 := $1B; // 設(shè)置SNTP數(shù)據(jù)報(bào)頭為SNTP 協(xié)議版本3,模式3(客戶機(jī)),即二進(jìn)制00011011; dt2ntp(now, ng.Xmit1, ng.xmit2);//將本機(jī)時(shí)間轉(zhuǎn)換為數(shù)據(jù)報(bào)時(shí)間格式; 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));// 將收到的數(shù)據(jù)報(bào)時(shí)間格式轉(zhuǎn)換為本機(jī)時(shí)間; DateTimeToSystemTime( result, SysTimeVar) ; SetLocalTime( SysTimeVar ); file://同步本地時(shí)間; MyDisconnect; except MyDisconnect; showmessage('時(shí)間同步失敗!'); application.Terminate; end; end; |
| procedure TForm1.MyDisconnect; file://關(guān)閉套接字; begin if MySocket <> INVALID_SOCKET then begin CloseSocket(MySocket); end; end; end. |
?
程序在Delphi3+ win98se 上通過。
附部分SMTP時(shí)間服務(wù)器網(wǎng)址:
| augean.eleceng.adelaide.edu.au* bernina.ethz.ch biofiz.mf.uni-lj.si* black-ice.cc.vt.edu chime.utoronto.ca* churchy.udel.edu (128.4.1.5)? clepsydra.dec.com clock.psu.edu clock.tricity.wsu.edu (192.31.216.30)? constellation.ecn.uoknor.edu cuckoo.nevada.edu* delphi.cs.ucla.edu dominator.eecs.harvard.edu |
總結(jié)
以上是生活随笔為你收集整理的Delphi开发环境中应用层网络协议的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GNU ARM汇编伪操作(Directi
- 下一篇: Log4cpp介绍及使用