设计模式与网络协议:状态模式管理连接状态、策略模式选择拥塞控制、责任链处理协议栈

网络协议栈,说白了就是一套「谁先谁后、谁管什么」的规则。我做了十几年网络中间件,发现很多人在写协议处理代码时,容易把逻辑揉成一团——一个函数里既判断连接状态,又选择拥塞算法,还要层层解析报文头。结果就是代码改不动、测不全、出问题还不好定位。

今天我们就用三个经典设计模式,把网络协议栈拆得清清楚楚。我个人习惯把这种组合叫做「协议三件套」:状态模式管状态、策略模式管算法、责任链模式管流程。

1. 状态模式:管理连接状态

网络连接的生命周期,说白了就是一堆状态的切换。TCP 有 LISTEN、SYN_SENT、ESTABLISHED、CLOSE_WAIT……每个状态下能做的事情完全不同。你想想看,如果在代码里写满 if-else 判断当前状态,那维护起来得多痛苦?

状态模式的核心思想很简单:把每个状态封装成一个独立的类。这样状态切换就是对象替换,而不是条件分支。

核心要点:状态模式让连接对象的行为随着内部状态的改变而改变,看起来就像对象「变脸」了一样。

Java 示例:TCP 连接状态管理

// 状态接口
interface TcpState {
    void open(TcpConnection conn);
    void close(TcpConnection conn);
    void send(TcpConnection conn, String data);
    void receive(TcpConnection conn, String data);
}

// 具体状态:已关闭
class ClosedState implements TcpState {
    public void open(TcpConnection conn) {
        System.out.println("打开连接");
        conn.setState(new EstablishedState());
    }
    public void close(TcpConnection conn) {
        System.out.println("连接已关闭,无需重复关闭");
    }
    public void send(TcpConnection conn, String data) {
        System.out.println("连接未打开,无法发送");
    }
    public void receive(TcpConnection conn, String data) {
        System.out.println("连接未打开,无法接收");
    }
}

// 具体状态:已建立
class EstablishedState implements TcpState {
    public void open(TcpConnection conn) {
        System.out.println("连接已建立,无需重复打开");
    }
    public void close(TcpConnection conn) {
        System.out.println("关闭连接");
        conn.setState(new ClosedState());
    }
    public void send(TcpConnection conn, String data) {
        System.out.println("发送数据: " + data);
    }
    public void receive(TcpConnection conn, String data) {
        System.out.println("接收数据: " + data);
    }
}

// 连接上下文
class TcpConnection {
    private TcpState state = new ClosedState();
    
    public void setState(TcpState state) {
        this.state = state;
    }
    
    public void open() { state.open(this); }
    public void close() { state.close(this); }
    public void send(String data) { state.send(this, data); }
    public void receive(String data) { state.receive(this, data); }
}

避坑指南:我曾经在一个项目中,把状态切换逻辑写在了连接对象里,结果状态一多,代码就变成了「面条式」的 if-else 嵌套。后来改成状态模式,每个状态类只关心自己该做什么,测试也容易写了。

2. 策略模式:选择拥塞控制算法

拥塞控制是 TCP 协议里最核心也最复杂的部分。不同的网络环境需要不同的算法:有线网络用 Cubic,无线网络用 BBR,数据中心用 DCTCP。你不可能在代码里写死一种算法,对吧?

策略模式就是干这个的:定义一组算法,把它们封装起来,让它们可以互相替换。这样拥塞控制算法的选择就变成了「插拔式」的。

C++ 示例:拥塞控制策略

// 策略接口
class CongestionControl {
public:
    virtual ~CongestionControl() = default;
    virtual void onPacketSent(int bytes) = 0;
    virtual void onPacketAcked(int bytes) = 0;
    virtual void onPacketLost() = 0;
    virtual int getCongestionWindow() const = 0;
};

// 具体策略:Cubic
class CubicControl : public CongestionControl {
private:
    int cwnd = 10;  // 初始拥塞窗口
public:
    void onPacketSent(int bytes) override {
        // Cubic 算法实现
    }
    void onPacketAcked(int bytes) override {
        cwnd += 1;  // 简化版:每收到一个 ACK 窗口加 1
    }
    void onPacketLost() override {
        cwnd = cwnd / 2;  // 乘法减小
    }
    int getCongestionWindow() const override {
        return cwnd;
    }
};

// 具体策略:BBR
class BbrControl : public CongestionControl {
private:
    int pacingRate = 100;  // 初始 pacing 速率
public:
    void onPacketSent(int bytes) override {
        // BBR 基于带宽和 RTT 探测
    }
    void onPacketAcked(int bytes) override {
        // 更新带宽估计
    }
    void onPacketLost() override {
        // BBR 对丢包不敏感,主要靠 pacing
    }
    int getCongestionWindow() const override {
        return pacingRate * 2;  // 简化版
    }
};

// 上下文:TCP 发送端
class TcpSender {
private:
    std::unique_ptr<CongestionControl> cc;
public:
    void setCongestionControl(std::unique_ptr<CongestionControl> newCc) {
        cc = std::move(newCc);
    }
    void sendData(int bytes) {
        cc->onPacketSent(bytes);
        // 实际发送逻辑
    }
    void onAck(int bytes) {
        cc->onPacketAcked(bytes);
    }
};

注意:策略模式虽然灵活,但不要滥用。我记得有个团队把每个数据包的处理都做成策略,结果策略类数量爆炸,反而增加了维护成本。策略模式适合「算法族」的场景,而不是「每个细节都策略化」。

3. 责任链模式:处理协议栈

网络协议栈是分层的:应用层 → 传输层 → 网络层 → 链路层。每一层只处理自己该做的事,处理完就传给下一层。这不就是责任链模式吗?

责任链模式的核心是:让多个对象都有机会处理请求,把它们连成一条链,沿着链传递请求直到有人处理

Java 示例:协议栈责任链

// 处理器抽象
abstract class ProtocolHandler {
    protected ProtocolHandler next;
    
    public void setNext(ProtocolHandler next) {
        this.next = next;
    }
    
    public abstract void handle(Packet packet);
    
    protected void passToNext(Packet packet) {
        if (next != null) {
            next.handle(packet);
        }
    }
}

// 应用层处理器
class ApplicationHandler extends ProtocolHandler {
    public void handle(Packet packet) {
        if (packet.getLayer() == Layer.APPLICATION) {
            System.out.println("应用层处理: " + packet.getData());
            // 处理后传递给传输层
            packet.setLayer(Layer.TRANSPORT);
            passToNext(packet);
        } else {
            passToNext(packet);
        }
    }
}

// 传输层处理器
class TransportHandler extends ProtocolHandler {
    public void handle(Packet packet) {
        if (packet.getLayer() == Layer.TRANSPORT) {
            System.out.println("传输层处理: 端口 " + packet.getPort());
            packet.setLayer(Layer.NETWORK);
            passToNext(packet);
        } else {
            passToNext(packet);
        }
    }
}

// 网络层处理器
class NetworkHandler extends ProtocolHandler {
    public void handle(Packet packet) {
        if (packet.getLayer() == Layer.NETWORK) {
            System.out.println("网络层处理: IP " + packet.getIp());
            packet.setLayer(Layer.LINK);
            passToNext(packet);
        } else {
            passToNext(packet);
        }
    }
}

// 链路层处理器
class LinkHandler extends ProtocolHandler {
    public void handle(Packet packet) {
        if (packet.getLayer() == Layer.LINK) {
            System.out.println("链路层处理: MAC " + packet.getMac());
            // 最终发送
        } else {
            passToNext(packet);
        }
    }
}

// 使用示例
public class ProtocolStack {
    public static void main(String[] args) {
        ApplicationHandler app = new ApplicationHandler();
        TransportHandler trans = new TransportHandler();
        NetworkHandler net = new NetworkHandler();
        LinkHandler link = new LinkHandler();
        
        app.setNext(trans);
        trans.setNext(net);
        net.setNext(link);
        
        Packet packet = new Packet("Hello", Layer.APPLICATION);
        app.handle(packet);
    }
}

我的经验:责任链模式在协议栈中特别好用,因为每一层的职责天然就是「要么处理,要么传递」。我曾经在一个物联网网关项目中,用责任链模式处理不同协议的转换——MQTT 转 CoAP、CoAP 转 HTTP,每层只关心自己的转换逻辑,代码清晰得不得了。

4. 三模式协同:完整的协议处理框架

这三个模式不是孤立的,它们可以协同工作。我画了一张图来展示它们的关系:

设计模式与网络协议:三模式协同框架 状态模式 管理连接状态 CLOSED SYN_SENT ESTABLISHED CLOSE_WAIT 策略模式 选择拥塞控制 Cubic BBR DCTCP Vegas 责任链模式 处理协议栈 应用层 传输层 网络层 链路层 状态模式管理「当前在哪」 策略模式决定「怎么走」 责任链模式安排「谁先走」

从图中可以看到:状态模式管「当前连接处于什么阶段」,策略模式管「当前阶段用什么算法」,责任链模式管「数据包经过哪些处理层」。三者各司其职,互不干扰。

5. 实战建议与避坑指南

最后,我分享几个实战中的经验:

  • 状态模式不要滥用:如果状态只有两三个,用 if-else 反而更简单。状态模式适合状态多、切换逻辑复杂的场景。
  • 策略模式注意性能:拥塞控制算法可能被频繁调用,策略对象的创建和切换要轻量。我一般用单例或者对象池来优化。
  • 责任链注意异常处理:链上任何一个环节出问题,都要有明确的错误传递机制。我曾经遇到过一个 bug,链路层抛了异常,结果应用层完全不知道,数据就丢了。
  • 三个模式可以嵌套:比如责任链的每个处理器内部,可以再用策略模式选择具体的处理算法。这种组合在真实项目中很常见。

总结一下:状态模式、策略模式、责任链模式,这三个模式组合起来,几乎能覆盖网络协议栈的所有核心逻辑。我个人觉得,理解它们之间的协作关系,比单独学每个模式更重要。你想想看,代码写出来是给人看的,结构清晰了,维护起来才不痛苦。

公众号:蓝海资料掘金营,微信deep3321