使用DELPHI做多層開發(fā)的朋友們都應該對Scktsrvr.exe這個程序不陌生的,
Borland公司在DELPHI中給出了它的源代碼。
這是一個900來行的程序,程序不算長,
現(xiàn)在我只選其中部分仔細讀一讀。
走的線路大致是,從服務器接到客戶端連接,處理客戶端的一個請求(這兒
選了客戶端向服務器發(fā)出的'取應用服務器列表'請求)
服務器接受了客戶端連接后,
因為ServerSocket采用的是阻塞模式,服務器執(zhí)行了下面這個線程來
服務客戶端:
//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: //有數(shù)據(jù)來了
begin
WSAResetEvent(Event);
Data := FTransport.Receive(False, 0); //從客戶端接收數(shù)據(jù)塊
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;
就這么舒舒服服的六十來行。
除開那些流程控制的語句,我們主要看到兩個東西:
數(shù)據(jù)傳輸(FTransport) 和 數(shù)據(jù)解析(FInterpreter)。
在本文中,我更感興趣的是它的應用協(xié)議的實現(xiàn),
數(shù)據(jù)傳輸(FTransport)就先扔在一邊,以后再看了.
現(xiàn)在我們就來看看它的數(shù)據(jù)解析部分。
。。。
FInterpreter := TDataBlockInterpreter.Create(Obj, SSockets) else
FInterpreter := TDataBlockInterpreter.Create(Obj, ');
//這兩種創(chuàng)建TDataBlockInterpreter類實例的方法的區(qū)別也不去管它.
。。。
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;//取出由客戶端傳來的數(shù)據(jù)塊中請求標志值
//客戶端數(shù)據(jù)塊的具體數(shù)據(jù)格式等會兒說.
if (Action and asMask) = asError then DoException(Data);
try
case (Action and asMask) of //根據(jù)客戶端的請求標志值作相應的處理.
//呵,就只有這么幾個。一個大型的MIDAS系統(tǒng)
//就全站在它們肩上.
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;
========================================================
哦..前面還有一個地方?jīng)]有說,就是這個神秘的Data的數(shù)據(jù)格式,它是以接口
形式提供的,
這個程序中用的是TDataBlock中實現(xiàn)的這個接口.
源碼中可以看出,TDataBlock中包含了一個Stream,
function TDataBlock.GetSize: Integer;
begin
Result := FStream.Size - BytesReserved;//BytesReserved的值這兒是8
end;
從這兒可以看出.數(shù)據(jù)塊是從Stream的第8個字節(jié)算起,前面就是兩個int型數(shù)的位置了.
function TDataBlock.GetSignature: Integer;
begin
FStream.Position := 0;
FStream.Read(Result, SizeOf(Result));
end;
呵, 沒錯, Stream的頭四字節(jié)就是它的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中包含數(shù)據(jù)的長度值.
需要提一下的是,如果你想改變一下傳輸數(shù)據(jù)塊的格式,比如給它加密加壓什么的,
中需要自己來實現(xiàn)IDataBlock接口,替掉TDataBlock就是了.
從這些源碼中可以得到很多東西,無論是從優(yōu)美的風格上,
無論是通訊程序開發(fā)還是MIDAS數(shù)據(jù)庫以及DCOM學習或應用者,
它都是值得一讀的源程序.
我覺得,更重要的是,它提供了一個嚴謹優(yōu)美和實際的范例,更給出了
一個靈活實用的框架體系。
halfdream(哈欠) 于2001-10-14晚
新聞熱點
疑難解答