此文講述的內容是一個實際項目開發中的一部分內容,筆者將親身經曆寫成文章。
【背景】
現需要實現這樣的功能:有多個用戶端連著同一個伺服器。伺服器和用戶端之間需要“互相”知道彼此的串連狀態。比如在某一時刻,伺服器需要知道當前有多少個用戶端正在和其通訊;某一個時刻,某個用戶端需要知道自己是否和伺服器保持串連。如果在某一時刻,一個用戶端關閉了,服務端應能及時感覺到;同樣,如果服務端被關閉,所有的用戶端應能及時感覺到,並作出一些反應。
【思考】
看到這個需求,直觀上的反應就是在服務端維護一個線上列表。當服務端的監聽器監聽到一個串連,就把該串連對應的用戶端資訊加入這個線上列表。這樣就完成了對上線狀況的記錄。但下一個問題是如何讓伺服器知道用戶端的離線狀況呢?我們可能會想到,讓用戶端在關閉前發送一個訊息到服務端,服務端收到訊息後就把用戶端置為離線狀態。但是,在更多情況下,用戶端並不是這麼“友好”地關閉的。應用程式崩潰、網路連接被重設、機器死機等情況下,用戶端來不及發送“離線通知”給服務端就掛掉了。這時,需要有一套機制,能讓服務端和用戶端彼此對對方的線上狀態保持清醒。
【概念】
何謂“心跳”? 心跳就是指“活著”的用戶端或服務端每隔一定的時間就互相發送接收一個訊息,告訴對方自己“活著”。當用戶端或服務端超過一定的時間間隔尚未收到對方的“心跳”訊息,就認為對方“死了”。這就是“心跳機制”的核心思想。
【設計實現】
在用戶端,除了 UI 外,需要三個線程在後台工作。
1,自動連接的線程。該線程可以實現每隔指定時間就檢查一次串連狀態,如果發現當前是“離線”狀態,就自動發起向服務端的一次串連。
1 private void ThreadConnect()
2 {
3 do
4 {
5
6 if (!_bConnected)
7 {
8 _bConnected = _sender.Connect(_ip, _port);
9
10 if (_bConnected)
11 {
12
13 Thread threadSendAndReceivePulseMessage = new Thread(new ThreadStart(ThreadSendAndReceivePulseMessage));
14 threadSendAndReceivePulseMessage.IsBackground = true;
15 threadSendAndReceivePulseMessage.Start();
16
17 Thread threadCheckPulseCount = new Thread(new ThreadStart(ThreadCheckPulseCount));
18 threadCheckPulseCount.IsBackground = true;
19 threadCheckPulseCount.Start();
20
21 _pulseCount = 0;
22
23 OnConnected(new EventArgs());
24 }
25
26 }
27 Thread.Sleep(_connectInterval);
28
29 }
30 while (_bWorking && _bAutoReconnect);
31 }
2,收發“心跳”訊息的線程。該線程和服務端進行收發心跳訊息。注意每收到伺服器發來的訊息,應將心跳計數器置零。心跳計數器的含義是已經隔了多少個心跳周期沒收到心跳訊息了。
1 private void ThreadSendAndReceivePulseMessage()
2 {
3 while (_bWorking && _bConnected)
4 {
5
6 string recv = _sender.Receive(64);
7
8
9 if (recv == "PULSE")
10 {
11 _pulseCount = 0;
12
13 _sender.Send("ALIVE");
14 }
15 else
16 {
17 _bConnected = false;
18 _sender.Close();
19
20 }
21 Thread.Sleep(10);
22 }
23 }
3,檢查心跳計數器的值的線程。該線程每隔指定的時間間隔就檢查一次心跳計數器,當發現已經超過指定心跳周期(比如3次)未接收到心跳訊息,就認為是離線了,則進行相應的處理。
1 private void ThreadCheckPulseCount()
2 {
3 while (_bWorking && _bConnected)
4 {
5 Thread.Sleep(_pulseInterval);
6
7 _pulseCount++;
8
9 if (_pulseCount > _maxPulseCount)
10 {
11 _bConnected = false;
12 _sender.Close();
13 }
14
15 if (!_bConnected)
16 {
17 OnDisconnected(new EventArgs());
18 }
19 }
20 }
在服務端,設計思想類似,需要維護一個“線上列表”,並及時和用戶端通訊,此處省略代碼。
【運行結果與測試】
開啟服務端,並開啟13個用戶端(為了測試聯機的時候的工作情況,使用了虛擬機器,在本機上開了6個用戶端,虛擬機器裡開了7個用戶端)。
此時關閉服務端,可以看到,所有的用戶端都掉線了。
再次啟動服務端,此時所有的用戶端都自動地串連。
仔細觀察,串連的順序每次並不相同,這是正常的。
直接關閉所有的用戶端。觀察服務端的輸出。
服務端也能感知所有的用戶端的離線情況。離線的順序和上線的順序並不一定一致,這是正常的。
over..