Delphi中编制分布式多层应用系统服务资源浏览器

http://tech.ccidnet.com/art/322/20021225/34765_1.html

 

本文详细介绍了在Delphi7中如何利用TSocketConnection控件设计开发一个通用的分布式多层应用系统客户端辅助开发工具——分布式多层应用系统服务资源浏览器。

在用Delphi开发分布式多层应用系统过程中,对于开发客户端程序的人员来说,了解分布式多层架构应用服务器所提供的资源(如:服务名称、提供者名称等)是一项经常遇到的工作。利用这些参数,客户端才能够正确地与服务器端程序连接并工作。

然而,在实际工作过程中,这些资源名称大都是以口头或E-Mail的方式,告知客户端开发人员;而一旦这些资源名称被改或者此类名称在一个开发服务器上较多时,就会发生程序不能与服务器正确连接的情况,或者造成服务名资源管理上的混乱。

另外,客户端开发人员只有在开发出客户端程序之后,才能够与服务器端程序进行连接并进行测试,没有前期通用的辅助工具与服务器进行交互,无形中增加了通信的开销,降低了开发工作的效率。

那么,有没有好的实现方法能够做到自动列出指定服务器上的开发资源名称,并能够与之进行动态地连接和交互呢?这就是笔者开发多层应用系统服务资源浏览器的原始初衷。

关键技术

对于分布式多层应用系统来说,客户端程序可通过DCOMConnection和SocketConnection控件与服务器端程序进行连接,这里我们使用SocketConnection进行连接工作。因为,SocketConnection控件不但可以通过IP地址来连接COM服务器,而且也可以主机名方式来连接COM服务器。

关于分布式多层应用系统服务资源浏览器实现的关键技术如下:

1.如何取得指定IP地址或主机名称上的服务资源名称列表

利用SocketConnection控件的GetServerList方法可取得服务器上的服务名称列表。详细信息见程序。

2.如何取得指定IP地址计算机,指定服务资源名称的提供者名称列表

利用SocketConnection的GetServer.AS_GetProviderNames来取得提供者名称列表。详细信息见程序。

通过以上方法,我们可以取得指定IP地址计算机上的服务名称列表和提供者名称列表,客户端就可以利用这些信息与服务器动态进行连接了。

程序具体实现

1.界面制作

在Delphi7中,制作如下图所示的界面。

说明:SocketConnection控件与服务器连接主要需要三个参数:Address(IP地址)或Host(主机名),端口号(默认是211,在服务器端可通过Borland Socket Server(scktsrvr.exe)程序设置监听的端口号),LoginPrompt(登录是否提示输入用户名和密码属性)。通过让用户输入的方式,可提高程序的灵活性。

2.程序实现中所用的公共函数和方法

//检验IP地址是否合法的函数
function IsLegalIp(Str: string): Boolean;
var
  I, K, DotCnt : Integer;
  Num: string;
  Arr: Array [1..4] of string;
begin
  Result := False;
  DotCnt := 0;
  //由'0'..'9', '.'组成
  For I := 1 to Length(Str) do
  begin
    if Not (Str[I] in ['0'..'9', '.']) then
      Exit
    else
    if Str[I] = '.' then
      inc(DotCnt);
  end;
  //点分隔符号数量应该=3
  if DotCnt <> 3 then Exit;
  For K := 1 to 3 do
  begin
    I := Pos('.', Str);
    Num := Copy(Str, 1, I - 1);
    Delete(Str, 1, I);
    Arr[K] := Num;
  end;
  Arr[4] := Str;
  try
    DotCnt := 0;
    For I := 1 to 4 do
    begin
      K := StrToInt(Arr[I]);
      //每字节的值应介于0~255之间
      if ((K >= 0) and (K <= 255)) then
        Inc(DotCnt);
    end;
    //四个字节都满足0~255之间,则合法
    if(DotCnt = 4) then
      Result := True;
  except
  end;
end;
//在窗体的private节中添加如下procedure SetStatusText(PanelId: Integer; Text: string);
procedure TMainFrm.SetStatusText(PanelId: Integer; Text: string);
begin
  StatusBar1.Panels[PanelId].Text := Text;
end;

3.在Caption为"取得服务列表"按钮的OnClick事件中书写如下代码:

//以下为该按钮OnClick事件处理代码
procedure TMainFrm.Button8Click(Sender: TObject);
var
  I : Integer;
  Cn: OleVariant;
  Str: string;
begin
  Button1.Enabled := False;    //设置连接按钮不可用
  Button2.Enabled := False;    //设置关闭按钮不可用
  try
    SocketCn.Close;
    ServerListBox.Items.Clear;
    if IsLegalIp(Edit1.Text) then
      SocketCn.Address := Edit1.Text
    else
      SocketCn.Host := Edit1.Text;
    //设置登陆是否提示
    SocketCn.LoginPrompt := CheckBox3.Checked;
    //设置端口号
    SocketCn.Port := SpinEdit1.Value;
    //取得服务列表
    Cn := SocketCn.GetServerList;
    //如何将OleVariant字符串数组加入ServerListBox中
    For I := VarArrayLowBound(Cn, 1) to VarArrayHighBound(Cn, 1) do
    begin
      Str := String(Cn[I]);
      ServerListBox.Items.Add(Str);
    end;
    Button1.Enabled := True;    //设置连接按钮可用
  except
    Raise;
  end;
end;

4.在Caption为"连接"按钮的OnClick写如下代码:

procedure TMainFrm.Button1Click(Sender: TObject);
var
  I : Integer;
  Name: string;
  Names: OleVariant;
  T1, T2: TTime;
begin
  if ServerListBox.ItemIndex = -1 then
  begin
    MessageBox(Handle, '请选择所使用的服务名称', '提示', Mb_Ok + Mb_IconInformation);
    Exit;
  end;
  SocketCn.Close;      //关闭,防止重复打开
  ProviderListBox.Items.Clear;
  try
    //指定SocketCn的服务名称
    SocketCn.ServerName := ServerListBox.Items[ServerListBox.ItemIndex];
    //计算连接时间
    T1 := Now();
    SocketCn.Open;
    T2 := Now();
    SetStatusText(1, '连接用时:' + TimeToStr(T2 - T1));
    //取得Provider
    if SocketCn.Connected then
    begin
      Cds.RemoteServer := SocketCn;
      T1 := Now();
      Names := SocketCn.GetServer.AS_GetProviderNames;
      T2 := Now();
      For I := VarArrayLowBound(Names, 1) to VarArrayHighBound(Names, 1) do
      begin
        Name := string(Names[I]);
        ProviderListBox.Items.Add(Name);
      end;
      SetStatusText(2, '取得Providers用时: ' + TimeToStr(T2 - T1));
    end;
    if SocketCn.Connected then
      Button2.Enabled := True;     //设置关闭按钮可用
  except
    Raise;
  end;
end;

5.在Caption为"关闭"按钮的OnClick写如下代码:

procedure TMainFrm.Button2Click(Sender: TObject);
begin
  if SocketCn.Connected then
  begin
    SocketCn.Close;
    ProviderListBox.Items.Clear;
  end;
  Button2.Enabled := False;    //设置Button2不可用
end;

6.在Caption为"打开SQL(有结果)"按钮的OnClick写如下代码:

//用于从所选的Provider中取得数据,对于查询可输入SQL语句,对于表可直接
//Open,即:CommandText := ''
procedure TMainFrm.Button3Click(Sender: TObject);
var
  T1, T2: TTime;
begin
  if ProviderListBox.ItemIndex <> -1 then
  begin
    try
      Cds.Close;         //关闭客户数据集控件
      Cds.ProviderName := ProviderListBox.Items[ProviderListBox.ItemIndex];
      T1 := Now();
      Cds.CommandText := Trim(Memo1.Text);
      Cds.Open;            //有结果集的打开方式
      T2 := Now();
      SetStatusText(3, '查询用时:' + TimeToStr(T2 - T1));
    except
      Raise;
    end;
  end
  else
    MessageBox(Handle, '请选择要使用的Provider', '提示', Mb_Ok + Mb_IconInformation);
end;

7.在Caption为"运行SQL(无结果)"按钮的OnClick写如下代码:

procedure TMainFrm.Button4Click(Sender: TObject);
var
  T1, T2: TTime;
begin
  if ProviderListBox.ItemIndex <> -1 then
  begin
    try
      Cds.Close;
      Cds.ProviderName := ProviderListBox.Items[ProviderListBox.ItemIndex];
      Cds.CommandText := Trim(Memo1.Text);       //设置SQL命令
      T1 := Now();
      Cds.Execute;          //没有返回结果的执行方式
      T2 := Now();
      SetStatusText(3, '查询用时:' + TimeToStr(T2 - T1));
    except
      Raise;
    end;
  end
  else
    MessageBox(Handle, '请选择要使用的Provider', '提示', Mb_Ok + Mb_IconInformation);
end;

8.在Caption为"保存数据"按钮的OnClick写如下代码,用于将

procedure TMainFrm.Button5Click(Sender: TObject);
begin
  if Cds.Active then
  begin
    With TSaveDialog.Create(Self) do
    begin
      if Execute then
      begin
        Cds.SaveToFile(FileName);
      end;
    end;
  end
  else
    MessageBox(Handle, '当前数据集尚未打开', '提示', Mb_Ok + Mb_IconInformation);
end;

9.在Caption为"装载数据"按钮的OnClick写如下代码:

procedure TMainFrm.Button6Click(Sender: TObject);
begin
  With TOpenDialog.Create(Self) do
  begin
    if Execute then
      try
        Cds.LoadFromFile(FileName);
      except
        Raise;
      end;
  end;
end;

10.Caption为"保存更新"按钮的OnClick写如下代码:

procedure TMainFrm.Button7Click(Sender: TObject);
begin
  Cds.ApplyUpdates(0);    //ClientDataSet保存更新
end;
程序运行结果

以下为在Windows98下本机测试的运行结果。在Windows2000Server和Internet网络测试环境下,也测试通过;因安全考虑,具体网络远程测试用IP地址恕不见告。

说明:

1、在运行此程序之前,服务器端程序必须保证在系统中运行过,以完成服务注册工作。

2、上图右下脚标题为Server的窗口为服务程序,在SocketCn连接时自动激活。

3、图中,若选择QueryProvider,则可以使用SQL脚本(包括数据语言---DDL);若选择TableProvider,则不用输入SQL语句(保证SQL命令为空),直接打开即可查看和编辑数据。

4、如果是本地测试,要首先运行Delphi\bin目录下的scktsrvr.exe,用于网络端口监听

和接收客户端的连接。

5、对于服务器端程序来说,支持SQL语句的Provider,必须设置其Options属性中的poAllowCommandText为True, 客户端才能使用SQL语句进行查询。

详细信息请查看附带的Server例。

结束语

此程序是一个通用的客户端多层应用系统开发辅助工具。通过在实际工作中使用此工具,对于服务器上的服务资源能够一目了然,大大方便了客户端的开发工作。

不仅如此,此程序提供了每种操作的时间统计信息,为实际工作中多层分布式应用系统性能的测试也提供了很好的手段。

此外,如果利用SQL命令可输入数据定义语句(DDL--Data Definition Language)的特点,此程序也可用于远程或并行数据库设计。

posted @ 2012-03-08 14:06  FoolRabbit  阅读(168)  评论(0)    收藏  举报