『Linux_网络』 基于状态机的Connect断线重连

发布于:2025-05-07 ⋅ 阅读:(11) ⋅ 点赞:(0)

        客户端会面临服务器崩溃的情况, 我们可以试着写一个客户端重连的代码, 模拟并理
解一些客户端行为, 比如游戏客户端等。

        客户端部分,我们本次采用状态机的设计模式实现

下面是关于状态机模式的介绍

状态机模式

状态机模式(State Pattern)是一种行为设计模式,它能让对象在内部状态发生改变时,改变自身的行为,给人一种对象仿佛改变了类的感觉。下面从多个方面对其进行详细介绍。

模式动机

在软件开发里,某些对象的行为会依据其状态的不同而改变。若用大量的条件语句(如 if-else 或 switch-case)来处理这些状态和行为,代码会变得复杂、难以维护和扩展。状态机模式把状态的行为封装在不同的状态类中,将状态转换逻辑和对象的其他行为分离,让代码结构更清晰,提升了可维护性和可扩展性。

模式结构

状态机模式主要包含以下几个角色:

  1. 上下文(Context):拥有一个状态对象的引用,负责与客户端交互,依据不同的情况调用状态对象的方法,并且能够在状态之间进行转换。
  2. 抽象状态(State):定义了一个接口,该接口包含了所有可能的状态行为,所有具体状态类都要实现这个接口。
  3. 具体状态(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断线重连,就结束了。

如有什么问题,欢迎评论区留言提问。


网站公告

今日签到

点亮在社区的每一天
去签到