读一读Scktsrvr.exe的源程序

转载 2007年10月09日 10:49:00
使用DELPHI做多层开发的朋友们都应该对Scktsrvr.exe这个程序不陌生的,
Borland公司在DELPHI中给出了它的源代码。
这是一个900来行的程序,程序不算长,
现在我只选其中部分仔细读一读。
走的线路大致是,从服务器接到客户端连接,处理客户端的一个请求(这儿
选了客户端向服务器发出的'取应用服务器列表'请求)


服务器接受了客户端连接后,
因为ServerSocket采用的是阻塞模式,服务器执行了下面这个线程来
服务客户端:


//SCKTMAIN.PAS

procedure TSocketDispatcherThread.ClientExecute;
var
  Data: IDataBlock;
  msg: TMsg;
  Obj: ISendDataBlock;
  Event: THandle;
  WaitTime: DWord;
begin
  CoInitialize(nil);                 //初始化COM
  try
    Synchronize(AddClient);             //在程序界面上显示客户信息,
        //用同步保证AddClient线程安全性
    FTransport := CreateServerTransport;
    try
      Event := FTransport.GetWaitEvent;
      PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
      GetInterface(ISendDataBlock, Obj);
      if FRegisteredOnly then
        FInterpreter := TDataBlockInterpreter.Create(Obj, SSockets) else
        FInterpreter := TDataBlockInterpreter.Create(Obj, '');
      try
        Obj := nil;
        if FTimeout = 0 then
          WaitTime := INFINITE else
          WaitTime := 60000;
        while not Terminated and FTransport.Connected do
        try
          case MsgWaitForMultipleObjects(1, Event, False, WaitTime, QS_ALLEVENTS) of
              //MsgWaitForMultipleObjects保持线程同步之用,
              //本文暂不细说它.
            WAIT_OBJECT_0:  //有数据来了
            begin
              WSAResetEvent(Event);
              Data := FTransport.Receive(False, 0);  //从客户端接收数据块
              if Assigned(Data) then
              begin
                FLastActivity := Now;
                FInterpreter.InterpretData(Data);//下面接着分析这儿
                Data := nil;
                FLastActivity := Now;
              end;
            end;
            WAIT_OBJECT_0 + 1:
              while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do
                DispatchMessage(msg);
            WAIT_TIMEOUT:
              if (FTimeout > 0) and ((Now - FLastActivity) > FTimeout) then
                FTransport.Connected := False;
          end;
        except
          FTransport.Connected := False;
        end;
      finally
        FInterpreter.Free;
        FInterpreter := nil;
      end;
    finally
      FTransport := nil;
    end;
  finally
    CoUninitialize;
    Synchronize(RemoveClient);
  end;
end;
就这么舒舒服服的六十来行。
除开那些流程控制的语句,我们主要看到两个东西:
数据传输(FTransport) 和  数据解析(FInterpreter)。

在本文中,我更感兴趣的是它的应用协议的实现,
数据传输(FTransport)就先扔在一边,以后再看了.

现在我们就来看看它的数据解析部分。
。。。
FInterpreter := TDataBlockInterpreter.Create(Obj, SSockets) else
FInterpreter := TDataBlockInterpreter.Create(Obj, '');
//这两种创建TDataBlockInterpreter类实例的方法的区别也不去管它.
。。。
 FInterpreter.InterpretData(Data);
。。。
就是这儿,就是这么一句。
它具体到底干了些什么呢??
================
。。。
 type
  TSocketDispatcherThread = class(TServerClientThread, ISendDataBlock)
  private
。。。
    FInterpreter: TDataBlockInterpreter;
    FTransport: ITransport;
。。。
 
================
FInterpreter这个对象引用就是这儿定义的。


procedure TDataBlockInterpreter.InterpretData(const Data: IDataBlock);
var
  Action: Integer;
begin
  Action := Data.Signature;//取出由客户端传来的数据块中请求标志值
        //客户端数据块的具体数据格式等会儿说.
  if (Action and asMask) = asError then DoException(Data);
  try
    case (Action and asMask) of     //根据客户端的请求标志值作相应的处理.
            //呵,就只有这么几个。一个大型的MIDAS系统
           //就全站在它们肩上.
      asInvoke: DoInvoke(Data);
      asGetID: DoGetIDsOfNames(Data);
      asCreateObject: DoCreateObject(Data);
      asFreeObject: DoFreeObject(Data);
      asGetServers: DoGetServerList(Data);
      asGetAppServers: DoGetAppServerList(Data);//取这个再进一步读一读.
    else
      if not DoCustomAction(Action and asMask, Data) then
        raise EInterpreterError.CreateResFmt(@SInvalidAction, [Action and asMask]);
    end;
  except
    on E: Exception do
    begin
      Data.Clear;
      WriteVariant(E.Message, Data);
      Data.Signature := ResultSig or asError;
      FSendDataBlock.Send(Data, False);
    end;
  end;
end;
==========================
顺着线一步步摸下去,
看它是怎么取APPSERVER列表返回客户端的。

很简单,就是通过GetMIDASAppServerList取本地的MIDAS应用服务
器列表,然后将其写在Data中,传回客户端就了事。
===========================
procedure TDataBlockInterpreter.DoGetAppServerList(const Data: IDataBlock);
var
  VList: OleVariant;
  List: TStringList;
  i: Integer;
begin
  Data.Clear;
  List := TStringList.Create;
  try
    GetMIDASAppServerList(List, FCheckRegValue);//取本机的应用服务器列表
          //想知道它是怎么做的吗?
          //源码上都有,自己看.
    if List.Count > 0 then
    begin
      VList := VarArrayCreate([0, List.Count - 1], varOleStr);
      for i := 0 to List.Count - 1 do
        VList[i] := List[i];
    end else
      VList := NULL;
  finally
    List.Free;
  end;
  WriteVariant(VList, Data);
  Data.Signature := ResultSig or asGetAppServers;
  FSendDataBlock.Send(Data, False);//将应用服务器列表传回客户端
end;

========================================================
哦..前面还有一个地方没有说,就是这个神秘的Data的数据格式,它是以接口
形式提供的,
这个程序中用的是TDataBlock中实现的这个接口.
源码中可以看出,TDataBlock中包含了一个Stream,
function TDataBlock.GetSize: Integer;
begin
  Result := FStream.Size - BytesReserved;//BytesReserved的值这儿是8
end;
从这儿可以看出.数据块是从Stream的第8个字节算起,前面就是两个int型数的位置了.
function TDataBlock.GetSignature: Integer;
begin
  FStream.Position := 0;
  FStream.Read(Result, SizeOf(Result));
end;
呵, 没错, Stream的头四字节就是它的Signature.客户端的请求标志就是放在这儿.

function TDataBlock.GetStream: TStream;
var
  DataSize: Integer;
begin
  FStream.Position := 4;
  DataSize := FStream.Size - BytesReserved;
  FStream.Write(DataSize, SizeOf(DataSize));
  FStream.Position := 0;
  Result := FStream;
end;
这儿很明显,就是Data中包含数据的长度值.

需要提一下的是,如果你想改变一下传输数据块的格式,比如给它加密加压什么的,
中需要自己来实现IDataBlock接口,替掉TDataBlock就是了.


从这些源码中可以得到很多东西,无论是从优美的风格上,
无论是通讯程序开发还是MIDAS数据库以及DCOM学习或应用者,
它都是值得一读的源程序.
我觉得,更重要的是,它提供了一个严谨优美和实际的范例,更给出了
一个灵活实用的框架体系。

 


                                             halfdream(哈欠) 于2001-10-14晚
 

读一读Scktsrvr.exe的源程序

读一读Scktsrvr.exe的源程序 使用DELPHI做多层开发的朋友们都应该对Scktsrvr.exe这个程序不陌生的, Borland公司在DELPHI中给出了它的源代码。 这是一个900...
  • aroc_lo
  • aroc_lo
  • 2013年06月25日 15:14
  • 626

(四)修改源程序代码

首先分成了两个类,Scope类和
  • u013116028
  • u013116028
  • 2014年08月02日 21:01
  • 308

C#代码统计源程序

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T...
  • py1994829
  • py1994829
  • 2015年04月09日 23:36
  • 316

可以输出自己的源程序代码(quine)

Quine 以哲学家 Willard van Orman Quine (1908-2000) 而命名,表示一个可以生成他自己的完全的源代码的程序。编写出某个语言中最简短的 quine 通常作为黑客们的...
  • benpaobagzb
  • benpaobagzb
  • 2015年09月14日 17:57
  • 184

关于源程序到可执行程序的过程

源程序,是指未经编译的,按照一定的程序设计语言规范书写的,人类可读的文本文件,我们通常理解为源程序就是我们所写好的代码。 可执行程序,我们常说的.exe程序,可以执行程序,完成计算机功能。在C语言中...
  • qq_26768741
  • qq_26768741
  • 2016年04月02日 13:43
  • 3744

汇编语言程序框架

详细内容请访问: https://www.liangking.me/2017/03/25/assembly-experiment2/   实验二 汇编语言程序框架       一、实验目的 l ...
  • LJ_King
  • LJ_King
  • 2017年04月08日 11:35
  • 566

高级语言程序的两种处理方式——编译和解释

编译方式         编译程序的功能就是把高级语言书写的源程序翻译成与之等价的目标程序(汇编语言或机器语言)。 编译程序的工作过程 词法分析         在词法分析阶段,源程序可以简单的看...
  • leimengyuanlian
  • leimengyuanlian
  • 2013年09月30日 20:17
  • 11186

【VS开发】MFC中调用C函数模块的解决方案

【VS开发】MFC中调用C函数模块的解决方案标签(空格分隔): 【VS开发】声明:引用请注明出处http://blog.csdn.net/lg1259156776/说明:最近调试基于MFC的程序,当通...
  • LG1259156776
  • LG1259156776
  • 2015年12月04日 12:16
  • 2295

30岁之前读一读这些书

网络包罗万象,凭借其丰富内容、易于浏览的文章和琳琅满目的作品,人们花在线阅读的时间越来越多。然而,网络并不能取代那些已出版(或即将出版)的经典权威书籍并使其中的深刻认知代代相承。      这里列...
  • duobstek
  • duobstek
  • 2015年02月11日 10:16
  • 252

计划读一读FreeBSD的源代码

最近总是计划读一读
  • be_water
  • be_water
  • 2014年07月14日 11:47
  • 116
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:读一读Scktsrvr.exe的源程序
举报原因:
原因补充:

(最多只允许输入30个字)