(接上文)
完善和修補(bǔ):
基于修正通過(guò)測(cè)試所發(fā)現(xiàn)的bug,和功能的完善,我們有對(duì)客戶端進(jìn)行了一定的改動(dòng),主要體現(xiàn)在:
·對(duì)客戶端進(jìn)行更好的異常處理,以防止由于服務(wù)器異常中斷而導(dǎo)致客戶端仍不端請(qǐng)求服務(wù)器所造成的死鎖。
·增加了說(shuō)話對(duì)象和悄悄話功能(在客戶端實(shí)現(xiàn))
·增加了登錄窗體,可以登陸到指定的房間并對(duì)服務(wù)器進(jìn)行配置(參看下面服務(wù)器的改進(jìn))
另外在服務(wù)器端我們也做了部分的改進(jìn),主要完成了上次沒有實(shí)現(xiàn)的功能,主要體現(xiàn)在:
·完成了服務(wù)器端任意配置并開放多個(gè)話題房間的功能(一個(gè)TchatRoom的實(shí)例對(duì)應(yīng)著一個(gè)話題房間)
·在服務(wù)器端的每個(gè)房間維護(hù)一份登錄進(jìn)房間的人員列表,供客戶端調(diào)用
·完善了服務(wù)器端的UI,并在服務(wù)器端實(shí)現(xiàn)為每個(gè)用戶的登錄和登出進(jìn)行向客戶系統(tǒng)公告的功能,并在服務(wù)器端限制登錄的人數(shù)和進(jìn)行重名判斷
我們來(lái)看看主要的改進(jìn)部分的代碼變化情況,首先是服務(wù)器端的接口:
  IChatManager = interface(IDispatch)
    ['{E7CD7F0D-447F-497A-8C7B-1D80E748B67F}']
    ……
    function GetRoomList: IStrings; safecall;//客戶端獲得服務(wù)器端的房間列表
    function RoomCanLogin(RoomID: Integer; const UserName: WideString): Integer; safecall;
//客戶端接收到一個(gè)返回值用以判斷服務(wù)器是否允許客戶登錄
    //返回值的表示:1:可以登陸 2:用戶重名 3:人數(shù)過(guò)多
    function RoomUserList(RoomID: Integer): IStrings; safecall;
//供客戶端獲得在一個(gè)房間內(nèi)的人員列表,由TchatRoom維護(hù)這個(gè)列表
    //每登錄和離開一個(gè)user便更新列表
end;
其中RoomCanLogin需要的實(shí)現(xiàn)比較重要,其余的兩個(gè)接口只是返回有服務(wù)器維護(hù)的兩個(gè)列表而已。
//RoomCanLogin方法對(duì)應(yīng)于TchatRoom類內(nèi)的實(shí)現(xiàn)
function TChatRoom.CanLogin(UserName:string): integer;
var
 i:integer;
begin
 result:=1;
 if FRoomUserList.Count>50 then //最多允許一個(gè)房間有50個(gè)人
 begin
  result:=3;
  exit;
 end;
 for i:=0 to FRoomUserList.Count-1 do
 //遍歷由TchatRoom維護(hù)的人員列表以判斷是否有重名用戶
 begin
  if FRoomUserList[i]=UserName then
   result:=2;
  break;
 end;
end;
再來(lái)看看,上次沒有實(shí)現(xiàn)的多話題房間維護(hù):
//請(qǐng)對(duì)比上篇文章的同名實(shí)現(xiàn)
constructor TChatRoomManager.Create;
var
 i:integer;
begin
 FRoomList:=TStringList.Create;
 try
  FRoomList.LoadFromFile(ExtractFilePath(application.ExeName)+'ChatRoomList.ini');
 except
  on E:Exception do
  begin
application.MessageBox(Pchar('配置文件錯(cuò)誤,錯(cuò)誤代碼:'+E.Message),'DComChat
   application.Terminate;
  end;
 end;
 FRoomList.Delete(0);
 FRoomCount:=FRoomList.Count;
 //這里將從配置文件中讀出有幾個(gè)聊天室
 setlength(ChatRoom,FRoomCount);
 for i:=1 to FRoomCount do
  ChatRoom[i]:=TChatRoom.Create(FRoomList[i-1],i);
  //創(chuàng)建房間的每一個(gè)實(shí)例
end;
客戶端的Timer.OnTimer的重要改進(jìn)(悄悄話和說(shuō)話對(duì)象的功能都在這里實(shí)現(xiàn)):
//請(qǐng)對(duì)比上篇文章的同名實(shí)現(xiàn)
procedure TClientMainForm.Timer1Timer(Sender: TObject);
var
 TempStrings:TStrings;
 i:integer;
 ToStartPos,ToEndPos:integer;
 FromWho,ToWho,TempName:string;
begin
 try
  if ChatServer.Server.ReadReady(RoomID)=1 then
  begin
   TempStrings:=TStringList.Create;
   SetOleStrings(TempStrings,ChatServer.Server.ReadFrom(RoomID));
   if FReadStartPos>19 then
    if (FClearBufferTag=0-ChatServer.Server.TestClearBufferTag(RoomID)) then
    begin
     FReadStartPos:=0;
     FClearBufferTag:=ChatServer.Server.TestClearBufferTag(RoomID);
    end;
   for i:=FReadStartPos to TempStrings.Count-1 do
   begin
    if RightStr(TempStrings[i],11)='SecretSpeak' then
    //可以看到實(shí)現(xiàn)悄悄話無(wú)非是在說(shuō)話內(nèi)容的最后加了一個(gè)特殊的標(biāo)示SecretSpeak
    begin
     //這一段程序從字符串中解析出說(shuō)話的對(duì)象
     ToStartPos:=pos(' 悄悄的對(duì) ',TempStrings[i]);
     FromWho:=Copy(TempStrings[i],1,ToStartPos-1);//誰(shuí)說(shuō)的
     ToStartPos:=ToStartPos+10;
     ToEndPos:=pos(' 說(shuō):',TempStrings[i]);
     ToWho:=Copy(TempStrings[i],ToStartPos,ToEndPos-ToStartPos);//說(shuō)給誰(shuí)
     ////////////////////////////////////////////////////////////////////////////////////////////////////
     if (ToWho='所有人') or (ToWho=UserName) or (FromWho=UserName) then
     //是自己說(shuō)的,或自己應(yīng)該看到的,或是說(shuō)給所有人的悄悄話都有權(quán)看到
     begin
      Memo1.Lines.Add(Copy(TempStrings[i],1,length(TempStrings[i])-11));
      Memo1.Lines.Add('');
     end;
    end
    else //不該看到的內(nèi)容
    begin
     Memo1.Lines.Add(TempStrings[i]);
     Memo1.Lines.Add('');
    end;
   end;
   FReadStartPos:=TempStrings.Count;
  end;
  //刷新在線人員列表
  Listbox1.Clear;
  SetOleStrings(ListBox1.Items,ChatServer.Server.RoomUserList(RoomID));
  //刷新說(shuō)話對(duì)象列表
  TempName:=SpeakToCBx.Text;
  SpeakToCBx.Clear;
  SpeakToCBx.Items.Assign(ListBox1.Items);
  SpeakToCBx.Items.Insert(0,'所有人');
  for i:=0 to SpeakToCBx.Items.Count-1 do
  begin
   if SpeakToCBx.Items[i]=TempName then Break;
  end;
  if i>SpeakToCBx.Items.Count-1 then i:=0;
  SpeakToCBx.ItemIndex:=i;
  //////////////////////////////////////////////////////////////////
 except //異常處理
  on E:Exception do
  begin
   Timer1.Enabled:=false;
   application.MessageBox
    (pchar('通信中斷或服務(wù)器故障,點(diǎn)確定后將關(guān)閉程序,請(qǐng)稍后重啟動(dòng)。詳細(xì)中斷原因:'+E.Message),'DCOMChatClient',MB_ICONWARNING);
   application.Terminate;
  end;
 end;
end;
當(dāng)然上面的程序所分析的字符串(說(shuō)給誰(shuí),誰(shuí)說(shuō)的,是否是悄悄話)都是在speak時(shí)產(chǎn)生的,這相當(dāng)?shù)暮?jiǎn)單:
//客戶端的speak
procedure TClientMainForm.Button1Click(Sender: TObject);
var
 content:string;
begin
 if Edit1.Text='' then
 begin
  application.MessageBox('不能發(fā)空消息。','DCOMChatClient',MB_ICONINFORMATION);
  exit;
 end;
 if length(edit1.Text)>100 then
 begin
  application.MessageBox('說(shuō)話內(nèi)容過(guò)長(zhǎng)。','DCOMChatClient',MB_ICONINFORMATION);
  exit;
 end;
 if CheckBox1.Checked then
  Content:=UserName+' 悄悄的對(duì) '+SpeakToCBx.Text+' 說(shuō):'+edit1.Text+'SecretSpeak'
  //可以看到悄悄話功能和說(shuō)話對(duì)象的功能只是在字符串上的簡(jiǎn)單處理罷了
 else
  Content:=UserName+' 對(duì) '+SpeakToCBx.Text+' 說(shuō):'+edit1.Text;
 ChatServer.Server.SpeakTo(Content,RoomID);
 edit1.Clear;
end;
至此這個(gè)程序已經(jīng)基本完善了,我們可以打包發(fā)布它,以免去最終用戶配置DCOM的麻煩。
(全文完)新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注