????????客戶端會面臨服務器崩潰的情況, 我們可以試著寫一個客戶端重連的代碼, 模擬并理
解一些客戶端行為, 比如游戲客戶端等。
? ? ? ? 客戶端部分,我們本次采用狀態機的設計模式實現
下面是關于狀態機模式的介紹
狀態機模式
狀態機模式(State Pattern)是一種行為設計模式,它能讓對象在內部狀態發生改變時,改變自身的行為,給人一種對象仿佛改變了類的感覺。下面從多個方面對其進行詳細介紹。
模式動機
在軟件開發里,某些對象的行為會依據其狀態的不同而改變。若用大量的條件語句(如?if-else
?或?switch-case
)來處理這些狀態和行為,代碼會變得復雜、難以維護和擴展。狀態機模式把狀態的行為封裝在不同的狀態類中,將狀態轉換邏輯和對象的其他行為分離,讓代碼結構更清晰,提升了可維護性和可擴展性。
模式結構
狀態機模式主要包含以下幾個角色:
- 上下文(Context):擁有一個狀態對象的引用,負責與客戶端交互,依據不同的情況調用狀態對象的方法,并且能夠在狀態之間進行轉換。
- 抽象狀態(State):定義了一個接口,該接口包含了所有可能的狀態行為,所有具體狀態類都要實現這個接口。
- 具體狀態(Concrete State):實現了抽象狀態接口,具體實現了在該狀態下的行為,并且在必要時可以改變上下文的狀態。
優點
- 可維護性:狀態的行為被封裝在不同的類中,使得代碼的維護和擴展更加容易。
- 可擴展性:如果需要添加新的狀態,只需要創建一個新的具體狀態類并實現相應的行為即可。
- 狀態轉換清晰:狀態轉換邏輯集中在上下文類中,使得狀態轉換更加清晰和易于管理。
缺點
- 類數量增加:如果狀態較多,會導致類的數量增加,增加了系統的復雜度。
- 狀態轉換邏輯復雜:如果狀態轉換邏輯復雜,上下文類中的狀態轉換代碼可能會變得復雜。
文檔編輯器樣例
下面給一個給一個文件編輯器的樣例,來幫助理解
假設你正在開發一個文檔編輯器,文檔有三種狀態:草稿(Draft)、審核中(Review)和已發布(Published)。文檔在不同的狀態下有不同的行為,比如草稿狀態可以編輯和提交審核,審核中狀態可以通過審核或者打回草稿,已發布狀態可以查看但不能編輯。
#include <iostream>// 前向聲明
class Document;// 抽象狀態類
class DocumentState {
public:virtual void edit(Document* document) {}virtual void submit(Document* document) {}virtual void approve(Document* document) {}virtual void reject(Document* document) {}virtual void view(Document* document) {}virtual ~DocumentState() {}
};// 具體狀態:草稿
class DraftState : public DocumentState {
public:void edit(Document* document) override {std::cout << "文檔處于草稿狀態,可以編輯。" << std::endl;}void submit(Document* document) override;
};// 具體狀態:審核中
class ReviewState : public DocumentState {
public:void approve(Document* document) override;void reject(Document* document) override;
};// 具體狀態:已發布
class PublishedState : public DocumentState {
public:void view(Document* document) override {std::cout << "文檔已發布,可以查看。" << std::endl;}
};// 上下文:文檔
class Document {
private:DocumentState* state;
public:Document();~Document();void setState(DocumentState* newState);void edit();void submit();void approve();void reject();void view();
};// DraftState 類的 submit 方法實現
void DraftState::submit(Document* document) {std::cout << "文檔已提交審核。" << std::endl;document->setState(new ReviewState());
}// ReviewState 類的 approve 方法實現
void ReviewState::approve(Document* document) {std::cout << "文檔審核通過,已發布。" << std::endl;document->setState(new PublishedState());
}// ReviewState 類的 reject 方法實現
void ReviewState::reject(Document* document) {std::cout << "文檔審核未通過,打回草稿。" << std::endl;document->setState(new DraftState());
}// Document 類的構造函數
Document::Document() {state = new DraftState();
}// Document 類的析構函數
Document::~Document() {delete state;
}// Document 類的 setState 方法
void Document::setState(DocumentState* newState) {delete state;state = newState;
}// Document 類的 edit 方法
void Document::edit() {state->edit(this);
}// Document 類的 submit 方法
void Document::submit() {state->submit(this);
}// Document 類的 approve 方法
void Document::approve() {state->approve(this);
}// Document 類的 reject 方法
void Document::reject() {state->reject(this);
}// Document 類的 view 方法
void Document::view() {state->view(this);
}int main() {Document document;document.edit();document.submit();document.approve();document.view();return 0;
}
Connect 斷線重連具體實現
Connect.hpp
?
?Client.cc
?測試Debug
測試部分,我采用我們在Linux網絡篇Tcp章節所編寫的翻譯服務器測試以及將實現客戶端與服務器通信的代碼放入process中
?編譯運行,服務器和客戶端之間就可以進行通信了
斷掉服務器,再啟動服務器,重新連接的服務也是支持的?
但是我們發現每次與服務器通信,都會有一個connect success... 的提示語,那么我們怎么去掉它呢?
?首先,我們需要明確為什么每次通信都會打印這個信息
這是因為,我們服務器與客戶端之間的通信循環走的是外面的這個狀態判斷,當狀態為CONNECTED的時候,我們正常執行process代碼,但當第二次循環的時候,由于我們連接狀態一直是成功的,所以再執行這里的時候,還是會打印這條信息,那我們應該怎么辦呢?
有兩種方法,第一種在設計一種狀態,就是當連接建立成功的時候,就只執行process,而不去打印這條提示語。
還記得我們之間設計的Connceting狀態嗎,當時說的可有可無,在這里我們就可以利用這個狀態,而不用去在取名一個新的狀態了。
像這樣
每次進入process,就設置狀態正在連接中。
在狀態循環中添加這樣的一條判斷
編譯運行,發現就沒有這樣的提示了
?
同樣也是支持斷線重連的?
?
但是這個方法也體現了狀態機的一個缺點,就是狀態數量的增加,狀態之間的復雜性問題。
第二種方法,就是將外面的循環挪到process中,這樣就不需要借助于狀態判斷的循環來維持服務器與客戶端之間的通信了?。
實現很簡單,就是在Process內部加一個循環循環條件是當前狀態為connected,修改狀態的時候,就可以自動退出了
?
編譯運行,各種功能也是支持的
?
?至此,我們的基于狀態機實現Client斷線重連,就結束了。
如有什么問題,歡迎評論區留言提問。