難度:★★★☆☆
先行知識(shí):Delphi / 接口 / OLE&COM&DCOM / Win32
 
首先祝大家新春快樂,猴年萬事如意!
在前面的幾篇文章中我們已經(jīng)探討了不少基于微軟COM的相關(guān)技術(shù),而分布式COM(以下簡(jiǎn)稱DCOM)的出現(xiàn)給我們輕松的創(chuàng)建分布式應(yīng)用提供了機(jī)會(huì);我們可以完全不去理會(huì)低級(jí)別的Windows Sockets(DCOM通過MS-RPC讓客戶與對(duì)象進(jìn)行通信,幸運(yùn)的是要開發(fā)COM應(yīng)用,開發(fā)者幾乎可以不去理會(huì)MS-RPC)而開發(fā)出功能強(qiáng)大、偶合性低(功能模塊相對(duì)獨(dú)立,很好的發(fā)揮了OO的思想)、易于部署的分布式計(jì)算系統(tǒng)。
在這次的文章中,我們就打算使用DCOM來開發(fā)一個(gè)局域網(wǎng)聊天室,不僅是作為技術(shù)上的研究,實(shí)際上我相信這應(yīng)該也是一個(gè)有用的工具。首先我們要對(duì)這個(gè)聊天室的功能有一個(gè)大致的了解:1、至少這個(gè)聊天室應(yīng)該允許多個(gè)局域網(wǎng)用戶進(jìn)行聊天。(有點(diǎn)廢話。。。)2、應(yīng)該能夠有多個(gè)話題的子聊天室,用戶可以選擇進(jìn)入某個(gè)聊天室進(jìn)行聊天。3、客戶端應(yīng)該盡量簡(jiǎn)單(不用配置DCOM),并需要一個(gè)服務(wù)器端管理所有的交互行為,管理聊天室的數(shù)目和相關(guān)配置,并做好系統(tǒng)監(jiān)測(cè)和日志記錄等。4、對(duì)聊天室功能進(jìn)行擴(kuò)展(如悄悄話功能,表情符號(hào)等)。根據(jù)以上的功能描述,在仔細(xì)分析問題以后我們?cè)O(shè)計(jì)出下面的草圖:

這篇文章中我們要大致實(shí)現(xiàn)這個(gè)程序的一個(gè)基本的核心,包括IChatManager、TChatRoomManager、TchatRoom,完成一個(gè)最基本功能的服務(wù)器端,并做一個(gè)簡(jiǎn)單的客戶端進(jìn)行檢測(cè)。我們的重點(diǎn)是服務(wù)器端,因?yàn)樗鼘?shí)現(xiàn)聊天室的大部分功能,客戶端只是一個(gè)十分小巧簡(jiǎn)單的程序。
由于篇幅關(guān)系,我們只列出重要的部分的代碼,完整的程序請(qǐng)給我發(fā)email。首先來看看我們的IchatManager接口是什么樣子(由于這里我們只實(shí)現(xiàn)了最基本的功能,這個(gè)接口并不完整,我們將在以后的文章中給出完整的聲明):
  IChatManager = interface(IDispatch)
    ['{E7CD7F0D-447F-497A-8C7B-1D80E748B67F}']
PRocedure SpeakTo(const content: WideString; destid: Integer); safecall;
//客戶向指定的房間說話,destid為房間號(hào)
    function ReadFrom(sourceid: Integer): IStrings; safecall;
//客戶從指定的房間讀取談話內(nèi)容,sourceid為房間號(hào)
    function ReadReady(id: Integer): Byte; safecall;
//客戶檢測(cè)指定的房間是否已經(jīng)可以讀取談話內(nèi)容
    procedure ConnectRoom(const UserName: WideString; RoomID: Integer); safecall;
//客戶登陸指定房間
    procedure DisconnectRoom(const UserName: WideString; RoomID: Integer); safecall;
//客戶退出指定房間
    function TestClearBufferTag(RoomID: Integer): Integer; safecall;
//客戶測(cè)試指定房間的緩沖區(qū)的清空與否狀況
  end;
再來看看接口的實(shí)現(xiàn)類TChatManager部分:
type
  TChatManager = class(TAutoObject, IChatManager)
  protected
    function ReadFrom(sourceid: Integer): IStrings; safecall;
//在這里我們使用Delphi擴(kuò)展的復(fù)雜類型TStings,為了讓COM支持這種
    //類型,delphi提供了IStrings接口
    procedure SpeakTo(const content: WideString; destid: Integer); safecall;
    function ReadReady(id: Integer): Byte; safecall;
//用來提供給客戶端查詢指定的房間是否可讀,既指定房間緩沖區(qū)是否為空
    procedure ConnectRoom(const UserName: WideString; RoomID: Integer);
      safecall;
    procedure DisconnectRoom(const UserName: WideString; RoomID: Integer);
      safecall;
function TestClearBufferTag(RoomID: Integer): Integer; safecall;
  end;
實(shí)現(xiàn)部分:
function TChatManager.ReadFrom(sourceid: Integer): IStrings;
var
 TempRoom:TChatRoom;
begin
 TempRoom:=ChatRoomManager.FindRoomByID(sourceid);
 while TempRoom.Locked do
 begin
//do nothing只是等待解鎖
 end;
 GetOleStrings(TempRoom.OneRead,Result);
end;
 
procedure TChatManager.SpeakTo(const content: WideString; destid: Integer);
var
 TempRoom:TChatRoom;
begin
 TempRoom:=ChatRoomManager.FindRoomByID(destid);
 while TempRoom.Locked do
 begin
//do nothing只是等待解鎖
 end;
 TempRoom.OneSpeak(content);
end;
 
function TChatManager.ReadReady(id: Integer): Byte;
var
 TempRoom:TChatRoom;
begin
 TempRoom:=ChatRoomManager.FindRoomByID(id);
 if TempRoom.CanRead then result:=1 else Result:=0;
end;
 
procedure TChatManager.ConnectRoom(const UserName: WideString;
  RoomID: Integer);
//客戶端通過接口登陸到指定的房間,沒有完全實(shí)現(xiàn)
var
 TempRoom:TChatRoom;
begin
 TempRoom:=ChatRoomManager.FindRoomByID(RoomID);
 TempRoom.LoginRoom(UserName);
end;
 
procedure TChatManager.DisconnectRoom(const UserName: WideString;
  RoomID: Integer);
//客戶端通過接口離開指定的房間,沒有完全實(shí)現(xiàn)
var
 TempRoom:TChatRoom;
begin
 TempRoom:=ChatRoomManager.FindRoomByID(RoomID);
 TempRoom.LeaveRoom(UserName);
end;
 
function TChatManager.TestClearBufferTag(RoomID: Integer): Integer;
var
 TempRoom:TChatRoom;
begin
 TempRoom:=ChatRoomManager.FindRoomByID(RoomID);
 result:=TempRoom.ClearBufferTag;
end;
 
initialization
  TAutoObjectFactory.Create(ComServer, TChatManager, Class_ChatManager,
    ciMultiInstance, tmApartment);
end.
比較關(guān)鍵TchatRoom是下面的樣子:
type
  TChatRoom=class
  private
   FBuffer:array[1..20] of string;
   FBufferLength:integer;
   FRoomName:string;
   FRoomID:integer;
   FLocked:boolean;//同步鎖,用來處理多人同時(shí)發(fā)出對(duì)話的情況
   FConnectCount:integer;//當(dāng)前房間的人數(shù)
FClearBufferTag:integer;
//每清空一次buffer此值便跳變一次,此脈沖被客戶端檢測(cè)
  protected
   procedure ClearBuffer;//清空緩沖區(qū)
   function GetCanRead:boolean;
  public
   constructor Create(RoomName:string;RoomID:integer);
   procedure OneSpeak(content:string);//將一條聊天內(nèi)容加入緩沖區(qū)
   procedure LoginRoom(UserName:string);//參看實(shí)現(xiàn)部分注釋
   procedure LeaveRoom(UserName:string);//參看實(shí)現(xiàn)部分注釋
   function OneRead:Tstrings;//從緩沖區(qū)中讀出記錄
   property Locked:boolean read FLocked; //readonly;//供IChatManager檢測(cè)
   property CanRead:boolean read GetCanRead;//判斷緩沖區(qū)是否為空,否則是不可讀的
   property ClearBufferTag:integer read FClearBufferTag;
  end;
(接后文)
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注