下面我們來看看具體實現流程:
聊天服務器的實現
我們的服務器的核心部分是ThreadedChatHandle類,我們需要處理的數據主要包括兩部分——在線列表和用戶發言。在線列表可以直接使用大的對象數組,這是基於一個聊天室容量是有限制的考慮。而用戶的發言直接發到管道裡面就可以了。
在線列表類的定義如下:
class Chater
{ private static Double id;//這個ID作為區別號,同時
private Double socketid; file://與聊天主幀對應的Socket相關聯。
private String nickname;// 用戶昵稱
private String passwd;// 用戶昵稱
private Int privilige;//
private String[] filter;//某個用戶的過濾列表
private Double login_time;//記錄登錄時間,用以清除一些超時聯接
private String color;//用戶聊天顏色
……//限於篇幅,省略了相關的方法。
}
注意:以上用戶數據大部分是在login階段,用戶通過身份驗證以後填入的。只有socketid要等到聊天主幀(一個普通的聊天界面包括聊天主幀,發言幀,在線列表幀三個部分)顯示以後才得到。如果超過一定時間,socketid還是沒有填入,說明浏覽器取得主框架以後連接中斷了,這時候就需要刪除該用戶數據。如果要實現象sohu那樣的私聊的話,還應該增加用戶IP地址的屬性。
用戶發言類的定義如下:
class Content
{ private Double timestamp;//時間戳
private Double fromChaterid;//發言人id
private Double toChaterid;//聊天對象id
private Boolen isSecurity;//是否私聊標志
private String theContent;//聊天內容,在構建器裡處理過,已經包括表情等ht
ml文本。
……//限於篇幅,省略了相關的方法。
}
核心的ThreadedChatHandle類主要處理的工作是分析用戶請求。客戶端發送的請求的值,主要有login(驗證身份,顯示聊天室主框架)、joinchat(初始化聊天信息,如顯示歡迎等,顯示聊天內容顯示幀,並保持連接,發送聊天信息。)、showtalk(顯示發言的幀)、names(顯示在線列表幀)、leave(用戶點擊按鈕離開聊天室)等等。
假如我們使用GET方法傳遞數據而不是通過POST方法提交表單的話,用戶數據輸入都是在URL裡傳送,下面是幾個url實例,結合後面客戶端流程,可以更好地理解ThreadedChatHandle類的職能:
這是一個用戶名密碼均為’aaa’的聊天用戶登錄系統,說了一句話“hello”,然後退出所產生的一系列請求:
/login?name=aaa&passwd=aaa
/joinchat?chaterid=555
/showtalk?chaterid=555
/names?chaterid=555
/speak?chaterid=555
/leave?chaterid=555
……
以上是服務器程序流程,實際上我們參數的傳遞不能只傳一個 chaterid,還需要有個對應的認證。而names傳遞一個chaterid是為了更新時間在線列表類內自己訪問的時間,避免連接超時。下面我們從客戶端看看具體登錄過程。
聊天界面由三個frame組成,其中joinchat幀是聊天內容顯示部分;showtalk幀是用戶輸入部分,包括聊天內容輸入、動作、過濾以及管理功能都在這一幀輸入;names是在線名單顯示部分,這一部分是定時刷新的。
讓我們從浏覽器的角度來看看進入聊天室的過程。
◆首先浏覽器請求頁面
http://host:8080/login?name=NAME&passwd=PWD
此時一個ThreadedChatHandle出現(包括了一個socket連接),並發送了一行數據:
GET /login?name=NAME&passwd=PWD HTTP/1.1
◆服務器生成一個session id,驗證密碼以後,發回:
HTTP/1.1 200 OK
<其他頭信息>
Content-TYPE: text/html
<空行>
<html>
……
<frameset cols="*,170" rows="*" border="1" framespacing="1">
<frameset rows="*,100,0" cols="*" border="0" framespacing="0">
<frame src="/joinchat?chaterid=555" name="u" frameborder="NO" noresize>
<frame src="/showtalk?chaterid=555" name="d" frameborder="NO" noresize>
</frameset>
<frame src="/names?chaterid=555" name="r" noresize>
</frameset>
……
</html>
然後ThreadedChatHandle.start()退出,本子線程結束
◆浏覽器收到以上html文件後,將會依次打開三個聯接(其中的chaterid是需要傳遞的變量,555是個虛指):
/joinchat?chaterid=555
/showtalk?chaterid=555
/names?chaterid=555
這三個聯接中的第一個聯接joinchat在整個聊天過程中都是保持聯接的,這樣從浏覽器角度來看,就是一個始終下載不完的大頁面,顯示效果上就是聊天內容不是靠刷新來更新,而是不斷地向上滾動。通過察看html代碼可以看到,只有<html><body>,然後就是不斷增加的聊天內容,沒有</body></html>。
另外兩個聯接在頁面發送完畢以後,處理這兩個連接的線程就結束了。
這樣一次登錄聊天室實際上有四個子線程響應,但登錄完成以後,只有處理joinchat幀的線程依然存活,用於接收來自服務器的聊天信息,這是基於推技術聊天室的關鍵所在。
當然,如果用戶有其它操作的請求,例如用戶注冊、修改昵稱、修改密碼等操作都可以通過類的擴充得到相對應的響應。通過對類方法的重載還可以比較方便的根據需要修改用戶認證機制與網站其它功能模塊結合在一塊。