1. DoIP实体激活与状态机
任务目标
实现一个DoIP实体的激活状态机,覆盖从监听、收到激活请求、执行激活到激活完成的完整流程,并处理激活过程中的超时与失败回退。本案例聚焦于DoIP实体激活的核心状态转换逻辑,不涉及完整TCP/TLS传输层实现。
状态定义
| 状态 | 含义 | 进入条件 | 超时时间 |
|---|---|---|---|
| LISTENING | 等待客户端激活请求 | 实体启动 | — |
| ACTIVATION_PENDING | 已收到激活请求,等待处理完成 | 收到有效激活请求 | 2s |
| ACTIVE | 激活完成,可处理DoIP消息 | 激活处理成功 | — |
| INACTIVE | 激活失败或超时后的回退状态 | 超时/处理失败 | — |
核心实现
#include <cstdint>
#include <functional>
#include <chrono>
#include <stdexcept>
enum class DoIPState : uint8_t {
LISTENING,
ACTIVATION_PENDING,
ACTIVE,
INACTIVE
};
enum class ActivationResult : uint8_t {
SUCCESS,
FAILURE,
TIMEOUT
};
class DoIPEntityActivator {
public:
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
explicit DoIPEntityActivator(std::function<bool()> activation_handler)
: activation_handler_(std::move(activation_handler)),
state_(DoIPState::LISTENING),
pending_since_(TimePoint{}) {}
// 收到客户端激活请求时调用
void onActivationRequest() {
if (state_ != DoIPState::LISTENING) {
return; // 非监听状态忽略新请求
}
state_ = DoIPState::ACTIVATION_PENDING;
pending_since_ = Clock::now();
}
// 周期性调用,驱动状态机推进
void tick() {
if (state_ != DoIPState::ACTIVATION_PENDING) {
return;
}
auto elapsed = Clock::now() - pending_since_;
if (elapsed > std::chrono::seconds(2)) {
state_ = DoIPState::INACTIVE;
return;
}
// 尝试执行激活处理
bool success = false;
try {
success = activation_handler_();
} catch (...) {
success = false;
}
if (success) {
state_ = DoIPState::ACTIVE;
} else {
state_ = DoIPState::INACTIVE;
}
}
// 外部主动重置实体(如收到关闭连接)
void reset() {
state_ = DoIPState::LISTENING;
pending_since_ = TimePoint{};
}
DoIPState getState() const { return state_; }
// 获取当前状态对应的激活结果(仅对终态有意义)
ActivationResult getActivationResult() const {
switch (state_) {
case DoIPState::ACTIVE: return ActivationResult::SUCCESS;
case DoIPState::INACTIVE: return ActivationResult::FAILURE;
default: throw std::logic_error("Not in terminal state");
}
}
private:
std::function<bool()> activation_handler_;
DoIPState state_;
TimePoint pending_since_;
};
验证方法
编写测试代码,依次验证以下场景:
- 正常激活:activation_handler返回true,调用onActivationRequest后立即tick,状态应为ACTIVE。
- 激活失败:activation_handler返回false,调用onActivationRequest后立即tick,状态应为INACTIVE。
- 超时回退:activation_handler内部阻塞超过2秒(或使用模拟时钟推进),状态应变为INACTIVE。
- 非监听状态忽略:在ACTIVE状态下再次调用onActivationRequest,状态不应改变。
通过条件:上述四个场景的状态转换结果与预期完全一致,且无异常抛出。
关键说明
- 激活处理函数由外部注入,本实现不关心具体激活逻辑(如TLS证书校验、路由表配置等),只负责状态编排与超时控制。
- 超时检测采用轮询方式(tick),实际工程中可替换为事件驱动或定时器回调,但状态转换逻辑不变。
- INACTIVE为终态,需通过reset()才能回到LISTENING,模拟DoIP实体重新初始化。