UDS 诊断会话控制(Diagnostic Session Control)详解与 C++ 实现 一、UDS 协议概述
UDS(Unified Diagnostic Services,统一诊断服务)是 ISO 14229 标准定义的一套应用于汽车电子控制单元(ECU)的诊断通信协议。它运行在 ISO 15765-2(CAN 传输层)或 DoIP(基于以太网的诊断)等底层协议之上,为整车诊断提供统一的请求/响应机制。
UDS 定义了大量服务,每个服务通过一个SID(Service Identifier,服务标识符)区分。常见服务包括:
SID
服务名称
0x10
DiagnosticSessionControl
诊断会话控制
0x11
ECUReset
ECU 复位
0x14
ClearDiagnosticInformation
清除诊断信息
0x19
ReadDTCInformation
读取 DTC 信息
0x22
ReadDataByIdentifier
按标识符读数据
0x27
SecurityAccess
安全访问
0x2E
WriteDataByIdentifier
按标识符写数据
0x31
RoutineControl
例程控制
0x3E
TesterPresent
诊断仪在线
二、诊断会话控制(0x10 服务)详解 2.1 为什么需要会话控制?
ECU 在正常运行时只响应有限的诊断请求。若任何时候都允许写入数据、刷写程序、读取敏感信息,将带来严重的安全风险和资源冲突。因此 UDS 引入了会话(Session)机制:
不同会话开放不同的服务集合:例如写入类服务仅在编程会话下可用。
敏感操作需要先进入受保护的会话:例如刷写需要进入编程会话并配合安全访问。
会话具有超时机制(S3 Timer):避免 ECU 长期处于开放状态。
会话类型
子功能值
Default Session(默认会话)
0x01
上电后自动进入,仅支持基础诊断服务
Programming Session(编程会话)
0x02
用于 Bootloader 刷写,安全性最高
Extended Diagnostic Session(扩展诊断会话)
0x03
支持标定、写入、例程控制等高级服务
Safety System Diagnostic Session(安全系统会话)
0x04
面向安全气囊等安全相关 ECU
0x40~0x5F 为用户自定义子功能范围。2.3 请求与响应格式
请求(Request):
Byte 0: 0x10 → SID
Byte 1: sessionType → 子功能(bit7 若置位表示抑制肯定响应 suppressPosRspMsgIndicationBit)
肯定响应(Positive Response):
Byte 0: 0x50 → SID + 0x40
Byte 1: sessionType → 回显子功能(低 7 位)
Byte 2~3: P2 Server Max → 服务器最大响应时间(ms)
Byte 4~5: P2* Server Max → 服务器增强响应时间(10ms 为单位)
否定响应(Negative Response):
Byte 0: 0x7F → 否定响应 SID
Byte 1: 0x10 → 原始请求 SID
Byte 2: NRC → 否定响应码
常见 NRC:
0x12Sub-function Not Supported(子功能不支持)0x13Incorrect Message Length(长度错误)0x22Conditions Not Correct(条件不满足)0x7ESub-function Not Supported In Active Session(当前会话不支持该子功能)0x7FService Not Supported In Active Session(当前会话不支持该服务)
上电
│
▼
┌────────────────┐ 0x10 0x03 ┌──────────────────┐
│ Default Session│──────────────▶│ Extended Session │
└────────────────┘ └──────────────────┘
▲ │
│ S3 超时 / ECUReset │ 0x10 0x02
│ ▼
│ ┌────────────────────┐
└────────────────────────│ Programming Session│
└────────────────────┘
S3 Timer:ECU 侧典型值 5s。若在该时间内未收到任何诊断请求,ECU 自动回到默认会话。
通常诊断仪会周期性发送
3E 80(TesterPresent 抑制肯定响应)来维持会话。
下面给出一个简化的、可编译运行的会话管理模块,涵盖:会话类型定义、请求解析、响应构造、S3 超时、服务可见性检查。
3.1 代码实现
3.2 使用示例(main.cpp)// uds_session.hpp
#pragma once
#include
#include
#include
#include
#include
#include
namespace uds {
// ============ 常量定义 ============
constexpr uint8_t SID_SESSION_CONTROL = 0x10;
constexpr uint8_t SID_SESSION_CONTROL_POS = 0x50;
constexpr uint8_t SID_NEGATIVE_RESPONSE = 0x7F;
constexpr uint8_t SID_TESTER_PRESENT = 0x3E;
constexpr uint8_t SUPPRESS_POS_RSP_BIT = 0x80;
// 否定响应码 NRC
enum class NRC : uint8_t {
GeneralReject = 0x10,
SubFunctionNotSupported = 0x12,
IncorrectMessageLength = 0x13,
ConditionsNotCorrect = 0x22,
RequestOutOfRange = 0x31,
SubFunctionNotSupportedInActiveSession = 0x7E,
ServiceNotSupportedInActiveSession = 0x7F,
};
// 会话类型
enum class SessionType : uint8_t {
Default = 0x01,
Programming = 0x02,
Extended = 0x03,
SafetySystem= 0x04,
};
const char* to_string(SessionType s) {
switch (s) {
case SessionType::Default: return "Default";
case SessionType::Programming: return "Programming";
case SessionType::Extended: return "Extended";
case SessionType::SafetySystem: return "SafetySystem";
default: return "Unknown";
}
}
// P2 / P2* 定时参数(单位:P2 为 ms,P2* 为 10ms)
struct TimingParams {
uint16_t p2_server_max; // ms
uint16_t p2_star_server_max; // 单位 10ms
};
// ============ 会话管理类 ============
class SessionManager {
public:
using Clock = std::chrono::steady_clock;
// S3 超时:5 秒
static constexpr std::chrono::milliseconds S3_TIMEOUT{5000};
SessionManager() : current_(SessionType::Default), last_activity_(Clock::now()) {}
// 处理接收到的诊断请求,返回响应字节序列(为空表示抑制响应)
std::vector handleRequest(const std::vector& request) {
// 任何请求都会刷新 S3 计时器
last_activity_ = Clock::now();
if (request.empty()) {
return makeNegative(0x00, NRC::IncorrectMessageLength);
}
uint8_t sid = request[0];
// TesterPresent 用于保活
if (sid == SID_TESTER_PRESENT) {
if (request.size() < 2) return makeNegative(sid, NRC::IncorrectMessageLength);
bool suppress = (request[1] & SUPPRESS_POS_RSP_BIT) != 0;
if (suppress) return {};
return { static_cast(SID_TESTER_PRESENT + 0x40),
static_cast(request[1] & 0x7F) };
}
if (sid == SID_SESSION_CONTROL) {
return handleSessionControl(request);
}
// 其它服务:此处仅作会话可见性检查示例
return makeNegative(sid, NRC::ServiceNotSupportedInActiveSession);
}
// 定时检查 S3 超时,需周期性调用(例如每 100ms)
void tick() {
if (current_ != SessionType::Default &&
(Clock::now() - last_activity_) > S3_TIMEOUT) {
std::cout << "[SessionManager] S3 timeout, fallback to Default\n";
current_ = SessionType::Default;
}
}
SessionType currentSession() const { return current_; }
private:
SessionType current_;
Clock::time_point last_activity_;
// ---- 处理 0x10 服务 ----
std::vector handleSessionControl(const std::vector& request) {
if (request.size() < 2) {
return makeNegative(SID_SESSION_CONTROL, NRC::IncorrectMessageLength);
}
uint8_t subFuncByte = request[1];
bool suppress = (subFuncByte & SUPPRESS_POS_RSP_BIT) != 0;
uint8_t sessionRaw = subFuncByte & 0x7F;
// 校验会话类型是否受支持
if (!isSupportedSession(sessionRaw)) {
return makeNegative(SID_SESSION_CONTROL, NRC::SubFunctionNotSupported);
}
SessionType target = static_cast (sessionRaw);
// 示例条件检查:进入编程会话要求当前是扩展会话
// (真实项目中还需先通过 0x27 安全访问)
if (target == SessionType::Programming &&
current_ != SessionType::Extended) {
std::cout << "[SessionManager] Deny -> Programming: must be in Extended first\n";
return makeNegative(SID_SESSION_CONTROL, NRC::ConditionsNotCorrect);
}
// 执行切换
std::cout << "[SessionManager] Switch: " << to_string(current_)
<< " -> " << to_string(target) << "\n";
current_ = target;
if (suppress) {
return {};
}
TimingParams tp = getTimingParams(target);
return {
SID_SESSION_CONTROL_POS,
sessionRaw,
static_cast((tp.p2_server_max >> 8) & 0xFF),
static_cast(tp.p2_server_max & 0xFF),
static_cast((tp.p2_star_server_max >> 8) & 0xFF),
static_cast(tp.p2_star_server_max & 0xFF),
};
}
static bool isSupportedSession(uint8_t s) {
return s == 0x01 || s == 0x02 || s == 0x03 || s == 0x04;
}
static TimingParams getTimingParams(SessionType s) {
switch (s) {
case SessionType::Default: return {50, 500}; // 50ms, 5000ms
case SessionType::Programming: return {50, 500}; // Boot 场景典型
case SessionType::Extended: return {50, 500};
case SessionType::SafetySystem: return {50, 500};
default: return {50, 500};
}
}
static std::vector makeNegative(uint8_t sid, NRC nrc) {
return { SID_NEGATIVE_RESPONSE, sid, static_cast(nrc) };
}
};} // namespace uds
3.3 预期输出#include "uds_session.hpp"
#include
#include
using namespace uds;
void printHex(const std::string& tag, const std::vector& data) {
std::cout << tag << ": ";
if (data.empty()) { std::cout << "(suppressed)\n"; return; }
for (auto b : data)
std::cout << std::hex << std::uppercase << std::setw(2)
<< std::setfill('0') << (int)b << " ";
std::cout << std::dec << "\n";
}
int main() {
SessionManager sm;
// 1) 请求进入扩展会话:10 03
printHex("REQ 10 03",
{0x10, 0x03});
auto rsp = sm.handleRequest({0x10, 0x03});
printHex("RSP ", rsp);
std::cout << "Current session: " << to_string(sm.currentSession()) << "\n\n";
// 2) 尝试直接进入编程会话(当前是扩展会话,应被拒绝?——这里扩展允许进入)
// 为了演示条件拒绝,先回到默认会话再尝试编程会话
sm.handleRequest({0x10, 0x01});
std::cout << "Current session: " << to_string(sm.currentSession()) << "\n\n";
// 3) 从默认会话直接进入编程会话 → 应被 NRC 0x22 拒绝
printHex("REQ 10 02", {0x10, 0x02});
rsp = sm.handleRequest({0x10, 0x02});
printHex("RSP ", rsp);
std::cout << "Current session: " << to_string(sm.currentSession()) << "\n\n";
// 4) 正确路径:默认 -> 扩展 -> 编程
sm.handleRequest({0x10, 0x03});
sm.handleRequest({0x10, 0x02});
std::cout << "Current session: " << to_string(sm.currentSession()) << "\n\n";
// 5) 抑制肯定响应:10 83
printHex("REQ 10 83", {0x10, 0x83});
rsp = sm.handleRequest({0x10, 0x83}); // 切到扩展,抑制响应
printHex("RSP ", rsp);
std::cout << "Current session: " << to_string(sm.currentSession()) << "\n\n";
// 6) 不支持的会话类型:10 09
printHex("REQ 10 09", {0x10, 0x09});
rsp = sm.handleRequest({0x10, 0x09});
printHex("RSP ", rsp);return 0;
}
四、关键设计要点总结REQ 10 03: 10 03
RSP : 50 03 00 32 01 F4
Current session: Extended
Current session: Default
REQ 10 02: 10 02
RSP : 7F 10 22
Current session: Default
Current session: Programming
REQ 10 83: 10 83
RSP : (suppressed)
Current session: ExtendedREQ 10 09: 10 09
RSP : 7F 10 12
要点
会话可见性
每个 SID 应定义在哪些会话下可用,请求前先判断当前会话
子功能抑制位
bit7 = 1 表示"若不产生否定响应则不发肯定响应",用于降低总线负载(如 TesterPresent 0x3E 80)
S3 超时
非默认会话必须实现 S3 定时器自动回退,避免 ECU 长期暴露于高权限会话
前置条件校验
进入编程会话前,通常要求先进入扩展会话 + 通过 0x27 安全访问
P2 / P2*
响应中返回给诊断仪的定时参数,诊断仪据此设置响应等待超时(典型 P2=50ms,P2*=5000ms)
否定响应优先
收到请求后若长度、会话、安全等条件不满足,应立即返回 0x7F + NRC
五、扩展方向
与 SecurityAccess(0x27)联动:为编程/扩展会话增加安全解锁前置校验。
服务权限表(Service Table):用
std::unordered_map描述每个 SID 允许的会话位图,实现通用分发。S3 定时器改用异步事件循环:在生产代码中通常由 OS 定时任务或状态机统一调度
tick()。集成 CAN/DoIP 传输层:将
handleRequest收到的载荷封装为 ISO-TP 多帧或 DoIP 报文。支持会话切换的副作用:例如从编程会话跳回默认会话时触发 ECUReset、DTC 状态更新等。
以上代码可作为 UDS 协议栈中会话管理层的原型,通过替换isSupportedSession、getTimingParams与条件判断,即可适配具体 ECU 项目的需求。
热门跟贴