(接上文)
完善和修補(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)注