通訊機制與流程圖
本單元以前一單元的TCP連線與離線程式為基礎,加入雙向通訊的程式,讓它變成真正的多人聊天室程式。前一單元的連線與離線動作只是客戶端將訊息單方向的傳給伺服器,伺服器收到後就直接處理上線或離線的動作,並不需要傳訊息給客戶,客戶端也只是發送訊息,不必接收訊息。但是本單元必須完成如上圖的兩種通訊流程,重點說明如下:
1.圖中伺服器裡面的ABCD代表已經上線的四個客戶的連線物件
2.廣播就是某客戶(客戶A)將訊息發到伺服器中屬於自己的連線物件(A),伺服器知道這是廣播之後會將訊息經過所有存在的連線物件傳給所有線上客戶(事實上也包括客戶A自己)。
3.如果是私密訊息,訊息本身就會有指定傳送的對象(客戶B),伺服器就依據指示找到目標(客戶B)的連線物件(B)將訊息送達目的。

伺服器需要更動或新增的程式
找到本機IP

複製上一單元的伺服器程式,先新增以下取得IP的副程式,並在Form_Load時顯示本機IP於TextBox1。

這與之前UDP單元做過的程式一樣,應該無須解釋了!目的是讓伺服器的IP顯示清楚方便跨機測試時使用!

傳送訊息給指定客戶的副程式 SendTo

與之前所學不同的是現在伺服器裡面有很多的連線可以選擇(一客戶一連線),所有連線物件會放在HT集合裡面,所以這個副程式會傳入使用者名稱(User)與要傳的訊息(Str),除了轉譯字串資訊為Byte陣列之外,也要依據使用者姓名自HT中拉出他(User)的連線物件 HT(User),並使用這個連線物件傳資料給該用戶。

傳送(廣播)訊息給所有客戶的副程式 SendAll

與前面的程式類似,用任意迴圈(For Each)將HT內所有的連線物件(Socket)一一取出,傳出同樣的訊息,就是廣播了!

建立線上人員名單的 OnlineList 副程式

當有客戶上線或離線時必須將新名單傳送給所有線上客戶,這就是建立目前名單的副程式。基本上是將所有名稱串聯成一個字串,每個名稱之間以逗點分隔。整個名單的最前端加上一個命令碼"L"這可以讓接收者知道這是線上名單,並作正確的更新名單處理。

監聽客戶的 Listen副程式
此部分在原本的上線("0")與離線("9")事件中必須加入一個 SendAll(OnlineList)的指令將新名單傳給所有線上客戶。
新增狀況包括客戶傳來的廣播與私密訊息,如同之前的上線("0")事件,與離線("9")事件,我們分別用"1"當作廣播的命令碼,"2"當作私密訊息的命令碼。廣播時用SendAll轉傳訊息給所有人,私密訊息則用SendTo傳給指定客戶。
程式碼修改完成後如下所示:

在此我們的客戶端是以命令碼"1"為廣播訊息,完整格式應寫成:"1+訊息"。
命令碼"2"為私密訊息,完整訊息應該寫成:"2+訊息+"|"+目標用戶,伺服器就可以用"|"這個特殊字元切開訊息與目標用戶,也就可以將訊息正確的傳到目的地了!這種訊息在上述程式中會交由Case Else處理,意思是碰到3,4,5...等等(除了"0","9","1"之外)命令碼時也會當作私密訊息傳遞,原因是我們在後續單元會使用這些命令碼當作不同遊戲的訊息傳遞,因為線上遊戲多半是兩個人對玩,對於伺服器來說在玩家之間轉傳遊戲訊息與聊天室私密訊息的程式碼其實是一樣的!只有在客戶端需要對不同遊戲訊號作不同的解讀。
這種設計表示我們可以不用改伺服器程式就可以用它當作多種遊戲的伺服器。可以省下很多時間,也減少很多不必要的錯誤機會!

客戶端應修改的程式
客戶端程式也是直接從上一單元複製,但是需要擴建的幅度比較大。
擴建表單介面

新增TextBox4做交談資訊看板,TextBox5作為發言輸入文字框,Button2是送出訊息按鍵,此按鍵預設應該是Enabled=False的,要等連線成功才能使用,廣播按鍵是Button3。右邊仿照伺服器也加上一個ListBox1用來顯示上線名單,這樣才能讓客戶指定要談話的對象!

加入執行緒命名空間

在此客戶端也要接聽訊息了!因此需要多執行緒,才能同時兼顧收與發的動作。

登入伺服器的程式與公用變數

比前一版本多出的是:
1.建立一個持續監聽的執行緒(Th),此執行緒設定為背景執行,程式在Listen副程式,也就是程式的資料處理中心。
2.因為有了多執行緒,需要加入忽略跨執行緒錯誤的設定,以免意外當掉。
3. Try語法可以讓連線失敗時顯示訊息並離開副程式避免直接當掉。
4.控制按鍵可否使用的時間,如連線之後就不能再次連線(Button1),連線成功時啟用發送訊息按鍵(Button2)。

監聽與回應訊息的 Listen 副程式

這邊與伺服器程式類似,也會有命令碼的分流處理機制,但是客戶實際上都是只從伺服器取得訊號,不需要分辨傳來訊息的是誰?程式相對簡單一點。比較不同之處說明如下:
1. 接收訊息的資料行加入Try的語法,這是當網路斷線或伺服器關閉時會產生的錯誤處理。斷線時應該將現物件T關閉,線上名單清除,顯示斷線的訊息,最後將本執行緒關閉。
*訊息的第一字元為命令碼:
2. "L"表示收到線上名單,先清除舊名單,再拆解名單字串為陣列M,一一加入ListBox1即可。
3. "1"表示收到公開訊息,加入看板標示為公開
4. "2"表示收到私密訊息,加入看板標示為私密

設定為廣播的程式 Button3

設定廣播就是不要選人,所以在此只是將選擇取消(ClearSelected)即可。

送出訊息的程式 Button2

這應該是最關鍵的程式碼了!如果ListBox1上面有選線上使用者,就是私密訊息,以命令碼"1"送出,且訊息後面應該加上選擇發送的對象,用"|"與訊息內容區隔。如果沒選對象就是廣播了,以命令碼"2"送出。當然此地也將自己的發言顯示於看板TextBox4。

關閉視窗離線的程式

與前一單元不同的是現在有監聽背景執行緒了!所以用Application.ExitThread()關閉所有執行緒,因為連線物件也在執行緒內使用T.Close的程式碼就可以省略了!

終於可以開始測試了!祝大家好運。