Scktsrvr.exe的源程序
以下内容摘自http://www.sudu.cn/info/html/edu/20071227/54284.html
读一读Scktsrvr.exe的源程序
使用DELPHI做多层开发的朋友们都应该对Scktsrvr.exe这个程序不陌生的,
Borland公司在DELPHI中给出了它的源代码。
这是一个Array00来行的程序,程序不算长,
现在我只选其中部分仔细读一读。
走的线路大致是,从服务器接到客户端连接,处理客户端的一个请求(这儿
选了客户端向服务器发出的取应用服务器列表请求)
服务器接受了客户端连接后,
因为ServerSocket采用的是阻塞模式,服务器执行了下面这个线程来
服务客户端:
1 //SCKTMAIN.PAS 2 3 procedure TSocketDispatcherThread.ClientExecute; 4 var 5 Data: IDataBlock; 6 msg: TMsg; 7 Obj: ISendDataBlock; 8 Event: THandle; 9 WaitTime: DWord; 10 begin 11 CoInitialize(nil); //初始化COM 12 try 13 Synchronize(AddClient); //在程序界面上显示客户信息, 14 //用同步保证AddClient线程安全性 15 FTransport := CreateServerTransport; 16 try 17 Event := FTransport.GetWaitEvent; 18 PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE); 19 GetInterface(ISendDataBlock, Obj); 20 if FRegisteredOnly then 21 FInterpreter := TDataBlockInterpreter.Create(Obj, SSockets) else 22 FInterpreter := TDataBlockInterpreter.Create(Obj, ); 23 try 24 Obj := nil; 25 if FTimeout = 0 then 26 WaitTime := INFINITE else 27 WaitTime := 60000; 28 while not Terminated and FTransport.Connected do 29 try 30 case MsgWaitForMultipleObjects(1, Event, False, WaitTime, QS_ALLEVENTS) of 31 //MsgWaitForMultipleObjects保持线程同步之用, 32 //本文暂不细说它. 33 WAIT_OBJECT_0: //有数据来了 34 begin 35 WSAResetEvent(Event); 36 Data := FTransport.Receive(False, 0); //从客户端接收数据块 37 if Assigned(Data) then 38 begin 39 FLastActivity := Now; 40 FInterpreter.InterpretData(Data);//下面接着分析这儿 41 Data := nil; 42 FLastActivity := Now; 43 end; 44 end; 45 WAIT_OBJECT_0 + 1: 46 while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do 47 DispatchMessage(msg); 48 WAIT_TIMEOUT: 49 if (FTimeout > 0) and ((Now - FLastActivity) > FTimeout) then 50 FTransport.Connected := False; 51 end; 52 except 53 FTransport.Connected := False; 54 end; 55 finally 56 FInterpreter.Free; 57 FInterpreter := nil; 58 end; 59 finally 60 FTransport := nil; 61 end; 62 finally 63 CoUninitialize; 64 Synchronize(RemoveClient); 65 end; 66 end;
就这么舒舒服服的六十来行。
除开那些流程控制的语句,我们主要看到两个东西:
数据传输(FTransport) 和 数据解析(FInterpreter)。
在本文中,我更感兴趣的是它的应用协议的实现,
数据传输(FTransport)就先扔在一边,以后再看了.
现在我们就来看看它的数据解析部分。
。。。
1 FInterpreter := TDataBlockInterpreter.Create(Obj, SSockets) else 2 FInterpreter := TDataBlockInterpreter.Create(Obj, ); 3 //这两种创建TDataBlockInterpreter类实例的方法的区别也不去管它. 4 //。。。 5 FInterpreter.InterpretData(Data); 6 //。。。 7 //就是这儿,就是这么一句。 8 //它具体到底干了些什么呢?? 9 //================ 10 。。。 11 type 12 TSocketDispatcherThread = class(TServerClientThread, ISendDataBlock) 13 private 14 //。。。 15 FInterpreter: TDataBlockInterpreter; 16 FTransport: ITransport; 17 //。。。 18 19 //================ 20 //FInterpreter这个对象引用就是这儿定义的。 21 22 23 procedure TDataBlockInterpreter.InterpretData(const Data: IDataBlock); 24 var 25 Action: Integer; 26 begin 27 Action := Data.Signature;//取出由客户端传来的数据块中请求标志值 28 //客户端数据块的具体数据格式等会儿说. 29 if (Action and asMask) = asError then DoException(Data); 30 try 31 case (Action and asMask) of //根据客户端的请求标志值作相应的处理. 32 //呵,就只有这么几个。一个大型的MIDAS系统 33 //就全站在它们肩上. 34 asInvoke: DoInvoke(Data); 35 asGetID: DoGetIDsOfNames(Data); 36 asCreateObject: DoCreateObject(Data); 37 asFreeObject: DoFreeObject(Data); 38 asGetServers: DoGetServerList(Data); 39 asGetAppServers: DoGetAppServerList(Data);//取这个再进一步读一读. 40 else 41 if not DoCustomAction(Action and asMask, Data) then 42 raise EInterpreterError.CreateResFmt(@SInvalidAction, [Action and asMask]); 43 end; 44 except 45 on E: Exception do 46 begin 47 Data.Clear; 48 WriteVariant(E.Message, Data); 49 Data.Signature := ResultSig or asError; 50 FSendDataBlock.Send(Data, False); 51 end; 52 end; 53 end; 54 //==========================
顺着线一步步摸下去,
看它是怎么取APPSERVER列表返回客户端的。
很简单,就是通过GetMIDASAppServerList取本地的MIDAS应用服务
器列表,然后将其写在Data中,传回客户端就了事。
===========================
1 procedure TDataBlockInterpreter.DoGetAppServerList(const Data: IDataBlock); 2 var 3 VList: OleVariant; 4 List: TStringList; 5 i: Integer; 6 begin 7 Data.Clear; 8 List := TStringList.Create; 9 try 10 GetMIDASAppServerList(List, FCheckRegValue);//取本机的应用服务器列表 11 //想知道它是怎么做的吗? 12 //源码上都有,自己看. 13 if List.Count > 0 then 14 begin 15 VList := VarArrayCreate([0, List.Count - 1], varOleStr); 16 for i := 0 to List.Count - 1 do 17 VList[i] := List[i]; 18 end else 19 VList := NULL; 20 finally 21 List.Free; 22 end; 23 WriteVariant(VList, Data); 24 Data.Signature := ResultSig or asGetAppServers; 25 FSendDataBlock.Send(Data, False);//将应用服务器列表传回客户端 26 end;
========================================================
哦..前面还有一个地方没有说,就是这个神秘的Data的数据格式,它是以接口
形式提供的,
这个程序中用的是TDataBlock中实现的这个接口.
源码中可以看出,TDataBlock中包含了一个Stream,
1 function TDataBlock.GetSize: Integer; 2 begin 3 Result := FStream.Size - BytesReserved;//BytesReserved的值这儿是8 4 end; 5 从这儿可以看出.数据块是从Stream的第8个字节算起,前面就是两个int型数的位置了. 6 function TDataBlock.GetSignature: Integer; 7 begin 8 FStream.Position := 0; 9 FStream.Read(Result, SizeOf(Result)); 10 end; 11 呵, 没错, Stream的头四字节就是它的Signature.客户端的请求标志就是放在这儿. 12 13 function TDataBlock.GetStream: TStream; 14 var 15 DataSize: Integer; 16 begin 17 FStream.Position := 4; 18 DataSize := FStream.Size - BytesReserved; 19 FStream.Write(DataSize, SizeOf(DataSize)); 20 FStream.Position := 0; 21 Result := FStream; 22 end;
这儿很明显,就是Data中包含数据的长度值.
需要提一下的是,如果你想改变一下传输数据块的格式,比如给它加密加压什么的,
中需要自己来实现IDataBlock接口,替掉TDataBlock就是了.
从这些源码中可以得到很多东西,无论是从优美的风格上,
无论是通讯程序开发还是MIDAS数据库以及DCOM学习或应用者,
它都是值得一读的源程序.
我觉得,更重要的是,它提供了一个严谨优美和实际的范例,更给出了
一个灵活实用的框架体系
posted on 2015-11-09 11:32 zhaoyuncai 阅读(950) 评论(0) 编辑 收藏 举报