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