日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Delphi开发环境中应用层网络协议的实现

發(fā)布時間:2023/12/20 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Delphi开发环境中应用层网络协议的实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
已經(jīng)進入Internet網(wǎng)絡時代了,許多新出的軟件都擁有網(wǎng)絡功能。其實,在這些軟件背后所依靠的技術基礎就是一系列的Inernet網(wǎng)絡協(xié)議標準,亦即TCP/IP系列協(xié)議。

  下面本人簡要介紹一下在Delphi環(huán)境下,直接采用winsock套接字編程,應用SNTP協(xié)議開發(fā)出具有網(wǎng)絡時間校準功能的應用,以此來說明如何在編程實踐中實現(xiàn)應用層網(wǎng)絡協(xié)議,相信感興趣的讀者能從中舉一反三。?

  一、程序原理

  1、 SNTP協(xié)議的運作機制 SNTP(簡單網(wǎng)絡時間協(xié)議)是在UDP協(xié)議基礎上發(fā)展出來的應用協(xié)議,目前廣泛應用于整個INTERNET上計算機時鐘的同步,依據(jù)同步源的性能及網(wǎng)絡路徑的差異,提供1~50ms的較準精度(資料來源:RFC-2030)。

  SNTP報文封裝見圖三,有關IP報頭和UDP報頭的詳細結構這里不再贅述,需要知道的是針對我們的SNTP應用,UDP報頭的源端口和目的端口值均應設定為123,SNTP數(shù)據(jù)緊跟在UDP報頭后(數(shù)據(jù)格式見圖二),SNTP協(xié)議應用分服務器端和客戶端,根據(jù)我們的應用需求只要考慮SNTP客戶端單播模式(UNICAST MODE)的運作:在該模式下,客戶端初始化NTP數(shù)據(jù)報,將SNTP數(shù)據(jù)頭的VN(協(xié)議版本號)值設置成3(即Version3);Mode值設置成3(即客戶端模式);數(shù)據(jù)區(qū)的Transmit Timestamp(傳輸時間戳)值設置成客戶機當前時間;然后向一個特定的單播模式時間服務器發(fā)出該數(shù)據(jù)報并接受服務器的回答,收到回答報文后從其中的傳輸時間戳里獲取同步時間。需要注意的是請求數(shù)據(jù)報初始化時其傳輸時間戳必須被設定為客戶機當前時間,這樣可以通過計算,消除服務器客戶機之間的傳播延遲,有效地將同步精度控制在10ms級范圍內(nèi)。

  2、用WINSOCK API實現(xiàn)SNTP 首先,調(diào)用socket(domain,type,protocol)建立套接字,其中domain 為 PF_INET(Internet域);type 為SOCK_DGRAM(UDP數(shù)據(jù)報); protpcol為IPPROTO_IP(IP協(xié)議)。其次,調(diào)用sendto(socket_id,buf,buflen,flags,to,tolen)發(fā)送SNTP客戶端請求,其中,buf為SNTP數(shù)據(jù);to為時間服務器名字,它是一結構,包含域名、端口號、32位主機地址等;socket_id為套接字號 。接著,調(diào)用recvfrom (socket_id,buf,buflen,flags,from,fromlen)接收SNTP數(shù)據(jù)回答,其中buf為SNTP數(shù)據(jù)回答。最后,將回答數(shù)據(jù)中的傳輸時間戳(Transmit Timestamp)轉換成Delphi的時間格式,并調(diào)用WINDOWS API函數(shù)SetLocalTime同步本地計算機時間,結束時關閉套接字。

  二、編程步驟:

  在IDE中建立一個新工程,缺省的Form上放一個label控件、一個button控件,在button控件的OnClick事件句柄中鍵入執(zhí)行同步時間函數(shù)及l(fā)abel控件時間顯示代碼,另外在單元文件中建立MySyncTime等5個有關時間同步函數(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(傳輸時間戳)
end;



// 用于轉換本機網(wǎng)絡字節(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;//主機名解析成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://時間服務器名字;
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ù)據(jù)報;
begin
SendTo(MySocket, s[1], Length(s), 0,Myaddr , sizeof(Myaddr));
end;
function TForm1.MyReceive; file://接收服務器時間數(shù)據(jù)報;
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)絡字節(jié)順序與本機字節(jié)順序轉換;
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; // 獲取本地時間區(qū)與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數(shù)據(jù)報初始化;
ng.head1 := $1B; // 設置SNTP數(shù)據(jù)報頭為SNTP 協(xié)議版本3,模式3(客戶機),即二進制00011011;
dt2ntp(now, ng.Xmit1, ng.xmit2);//將本機時間轉換為數(shù)據(jù)報時間格式;
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ù)報時間格式轉換為本機時間;
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.

  ?
  程序在Delphi3+ win98se 上通過。

  附部分SMTP時間服務器網(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

總結

以上是生活随笔為你收集整理的Delphi开发环境中应用层网络协议的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。