读一读Scktsrvr.exe的源程序

使用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晚

http://blog.csdn.net/aroc_lo/article/details/9170229

读一读Scktsrvr.exe的源程序的更多相关文章

  1. [No000060]冷读热读:读书九问

    兵无常势,水无常形,读书亦无法.彼之砒霜,我之佳肴.然读书无法却有道.你我都是使用同一颗大脑在读书.这颗大脑受制于那千千万万年以来,星辰起落,狩猎采集,演化大道. Q1:读物如何分级? 坏书.可用的书 ...

  2. Hadoop源码学习笔记(1) ——第二季开始——找到Main函数及读一读Configure类

    Hadoop源码学习笔记(1) ——找到Main函数及读一读Configure类 前面在第一季中,我们简单地研究了下Hadoop是什么,怎么用.在这开源的大牛作品的诱惑下,接下来我们要研究一下它是如何 ...

  3. MySQL——一致性非锁定读(快照读)&MVCC

    MySQL--一致性非锁定读(快照读) MySQL数据库中读分为一致性非锁定读.一致性锁定读 一致性非锁定读(快照读),普通的SELECT,通过多版本并发控制(MVCC)实现. 一致性锁定读(当前读) ...

  4. ORACLE 物理读 逻辑读 一致性读 当前模式读总结浅析

    在ORACLE数据库中有物理读(Physical Reads).逻辑读(Logical Reads).一致性读(Consistant Get).当前模式读(DB Block Gets)等诸多概念,如果 ...

  5. 数据库 一致性读&&当前读

    今天小伙伴问了一个sql的问题: update t set status=2 where id in(select id from t where status=1) 这个sql,在并发的情况下,会不 ...

  6. 读着读着《构建之法》(Build To Win) 越精神的白雪儿的思考

    哲学家的宗旨是:我思,故我在 科学家的宗旨是:我发现,故我在 工程师的宗旨是:我构建,故我在 ——<工程学--无尽的前沿> 序言:珍惜角色“人”,注重实践“物” <构建之法>, ...

  7. SQLSERVER预读逻辑读物理读

    预读:用估计信息,去硬盘读取数据到缓存.预读100次,也就是估计将要从硬盘中读取了100页数据到缓存. 物理读:查询计划生成好以后,如果缓存缺少所需要的数据,让缓存再次去读硬盘.物理读10页,从硬盘中 ...

  8. 今天读一读七天学会NodeJS

    七天学会NODEJS NodeJS基础 小结 本章介绍了有关NodeJS的基本概念和使用方法,总结起来有以下知识点: NodeJS是一个JS脚本解析器,任何操作系统下安装NodeJS本质上做的事情都是 ...

  9. 利用ScktSrvr打造多功能Socket服务器

    Socket服务端编程中最重要的也是最难处理的工作便是客户请求的处理和数据的接收和发送,如果每一个Socket服务器应用程序的开发都要从头到尾处理这些事情的话,人将会很累,也会浪费大量时间.试想,如果 ...

随机推荐

  1. QSettings保存程序设置

    今天看了一些QSettings的简单用法,可以用来保存程序的设置,使得程序每次启动都可以显示上次关闭时的状态.我这里实现了一个简单的文本编辑窗口,可以设置文本的字体,字体的颜色和背景色.每次关闭程序都 ...

  2. ESRI Shapefiles (SHP)

    ESRI Shapefiles (SHP) Also known as ESRI ArcView Shapefiles or ESRI Shapefiles. ESRI is the company ...

  3. 使用linq对字符串1,2,3,4,5,6,7,8,9,10求和

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SumI ...

  4. 《大象UML》看书笔记2:

    <大象UML>看书笔记2 抽象角度:                                                        在为现实世界建模的时候,首先要搞清楚有多 ...

  5. C语言,C++,static

    术语static有着不寻常的历史.起初,在C中引入关键字static是为了表示退出一个块后仍然存在的局部变量.随后,static在C中有了第二种含义:用来表示不能被其它文件访问的全局变量和函数.为了避 ...

  6. EL显示List里嵌套map(Spring MVC3)返回的model

    <c:forEach items="${requestScope.courseList}" var="cMap" varStatus="stat ...

  7. perl json模块

    JSON - JSON (JavaScript Object Notation) encoder/decoder 简介: use JSON; # imports encode_json, decode ...

  8. encode_utf8 把字符编码成字节 decode_utf8解码UTF-8到字符

    encode_utf8 $octets = encode_utf8($string); Equivalent to "$octets = encode("utf8", $ ...

  9. 四个流行的Java连接池之Proxool篇

    Proxool是一个JavaSQL Driver驱动程序,提供了对你选择的其它类型的驱动程序的连接池封装.可以非常简单的移植到现存的代码中.完全可配置.快速,成熟,健壮.可以透明地为你现存的JDBC驱 ...

  10. VB.net总结

    .NET视频差点儿相同用时一周结束,总体感觉就是"走耳不走脑".刚開始看视频理解起来有一点困难,台湾口音以及台湾与大陆计算机术语的差异,让我把前几集相当于直接忽略过了(建议打算看这 ...