程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 讀一讀Scktsrvr.exe的源程序

讀一讀Scktsrvr.exe的源程序

編輯:Delphi

  使用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學習或應用者,
  它都是值得一讀的源程序.
  我覺得,更重要的是,它提供了一個嚴謹優美和實際的范例,更給出了
  一個靈活實用的框架體系。

   

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved