读一读Scktsrvr.exe的源程序

    技术2022-05-11  101

    使用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的值这儿是8end;从这儿可以看出.数据块是从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学习或应用者,它都是值得一读的源程序.我觉得,更重要的是,它提供了一个严谨优美和实际的范例,更给出了一个灵活实用的框架体系。

     

    最新回复(0)