网易首页 > 网易号 > 正文 申请入驻

UDS 诊断会话控制(Diagnostic Session Control)详解与 C++ 实现

0
分享至


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 长期处于开放状态。

2.2 常见会话类型

会话类型

子功能值

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(当前会话不支持该服务)

2.4 会话切换流程(含 S3 超时)

        上电
│
▼
┌────────────────┐ 0x10 0x03 ┌──────────────────┐
│ Default Session│──────────────▶│ Extended Session │
└────────────────┘ └──────────────────┘
▲ │
│ S3 超时 / ECUReset │ 0x10 0x02
│ ▼
│ ┌────────────────────┐
└────────────────────────│ Programming Session│
└────────────────────┘
  • S3 Timer:ECU 侧典型值 5s。若在该时间内未收到任何诊断请求,ECU 自动回到默认会话。

  • 通常诊断仪会周期性发送3E 80(TesterPresent 抑制肯定响应)来维持会话。

三、C++ 代码实现

下面给出一个简化的、可编译运行的会话管理模块,涵盖:会话类型定义、请求解析、响应构造、S3 超时、服务可见性检查。

3.1 代码实现

// 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.2 使用示例(main.cpp)

#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;
}
3.3 预期输出

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: Extended

REQ 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


五、扩展方向

  1. 与 SecurityAccess(0x27)联动:为编程/扩展会话增加安全解锁前置校验。

  2. 服务权限表(Service Table):用std::unordered_map 描述每个 SID 允许的会话位图,实现通用分发。

  3. S3 定时器改用异步事件循环:在生产代码中通常由 OS 定时任务或状态机统一调度tick()。

  4. 集成 CAN/DoIP 传输层:将handleRequest收到的载荷封装为 ISO-TP 多帧或 DoIP 报文。

  5. 支持会话切换的副作用:例如从编程会话跳回默认会话时触发 ECUReset、DTC 状态更新等。

以上代码可作为 UDS 协议栈中会话管理层的原型,通过替换isSupportedSession、getTimingParams与条件判断,即可适配具体 ECU 项目的需求。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
孙千挽着刘诗诗那一刻,连空气都变软了

孙千挽着刘诗诗那一刻,连空气都变软了

草莓解说体育
2026-10-09 06:36:03
当年扬言"非千万富翁不嫁"的唐治萍,如今每天捆600只螃蟹

当年扬言"非千万富翁不嫁"的唐治萍,如今每天捆600只螃蟹

喜欢历史的阿繁
2026-10-01 01:08:50
中日联合声明承诺台湾问题,高市早苗却扬言动武,想要撕毁承诺?

中日联合声明承诺台湾问题,高市早苗却扬言动武,想要撕毁承诺?

观锐器
2026-10-08 15:33:15
新游尺度太大害惨网红!19万粉丝账号永封 收入全断

新游尺度太大害惨网红!19万粉丝账号永封 收入全断

游民星空
2026-10-09 23:02:45
《巫师3RE》也遇上鬼脸BUG了!免费升级那还说啥

《巫师3RE》也遇上鬼脸BUG了!免费升级那还说啥

游民星空
2026-10-07 17:23:09
【2026.10.9】扒酱料不停:那些你不知道的八卦一二三

【2026.10.9】扒酱料不停:那些你不知道的八卦一二三

娱乐真爆姐
2026-10-10 00:39:19
最差劲的美国总统是谁,不是拜登、不是特朗普,而是恶行累累的他

最差劲的美国总统是谁,不是拜登、不是特朗普,而是恶行累累的他

郭蛹包工头
2026-10-08 21:56:05
2013年,只因丈夫没尝过处女的滋味,怀孕的妻子诱骗花季女孩给丈夫享用

2013年,只因丈夫没尝过处女的滋味,怀孕的妻子诱骗花季女孩给丈夫享用

法网恢恢
2026-08-17 23:40:30
美国注射死刑失败女囚奇迹幸存,苏醒后情绪高度愤怒,先问“我在哪里?你是谁?”,二次执行死刑极可能违宪

美国注射死刑失败女囚奇迹幸存,苏醒后情绪高度愤怒,先问“我在哪里?你是谁?”,二次执行死刑极可能违宪

极目新闻
2026-10-09 10:10:32
大开眼界!许家印当年奢靡生活曝光

大开眼界!许家印当年奢靡生活曝光

麦杰逊
2026-08-23 15:09:25
伯特利:尊界非公司客户,未向V800供应刹车踏板

伯特利:尊界非公司客户,未向V800供应刹车踏板

澎湃新闻
2026-10-09 09:58:26
女人生理性喜欢一个男人,不管多大年纪,都会有这些表现

女人生理性喜欢一个男人,不管多大年纪,都会有这些表现

小影的娱乐
2026-10-07 00:38:49
援助变参战?俄方怒下通牒,首个参战国被锁定,北约防线岌岌可危

援助变参战?俄方怒下通牒,首个参战国被锁定,北约防线岌岌可危

史之韵
2026-08-22 02:30:38
最高法:回村定居的退休人员,有权重修祖宅或申请宅基地建房

最高法:回村定居的退休人员,有权重修祖宅或申请宅基地建房

智慧生活笔记
2026-10-01 09:54:07
这些外国学者之所以能获得诺贝尔奖,根本原因是他们是外国学者

这些外国学者之所以能获得诺贝尔奖,根本原因是他们是外国学者

必记本
2026-10-07 19:57:09
今日最惨股,已暴跌96%,今又一字跌停,超500万跌停封单!

今日最惨股,已暴跌96%,今又一字跌停,超500万跌停封单!

财经智多星
2026-10-09 11:01:07
湖南永州周某某,人美心善情人多,不应该过度关注她的私生活

湖南永州周某某,人美心善情人多,不应该过度关注她的私生活

汉史趣闻
2026-10-09 15:30:19
晋妈和优优之间早已有了裂痕

晋妈和优优之间早已有了裂痕

阿废冷眼观察所
2026-10-09 00:39:46
金与正:韩国声称要对朝医疗援助系“严重挑衅”

金与正:韩国声称要对朝医疗援助系“严重挑衅”

新京报
2026-10-08 15:50:14
原来李诚儒前妻早已离世,难怪父子关系缓和,41岁李大海已成骄傲

原来李诚儒前妻早已离世,难怪父子关系缓和,41岁李大海已成骄傲

仙味少女心
2026-10-09 23:38:42
2026-10-10 02:04:49
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1024文章数 348关注度
往期回顾 全部

科技要闻

华为小米手机同日涨价,最高涨1000元

头条要闻

七旬大伯在浙江买山景别墅养老 住进去后天天提心吊胆

头条要闻

七旬大伯在浙江买山景别墅养老 住进去后天天提心吊胆

体育要闻

与C罗硬碰硬,一个72岁老人的倔强

娱乐要闻

吴奇隆宣布重要决定,走上谢霆锋老路

财经要闻

时隔12年,公募基金运作办法修订

汽车要闻

2027款深蓝L06及追风版上市 限时权益价11.49万元起

态度原创

健康
本地
房产
旅游
军事航空

痘坑留下后,还能填平吗?

本地新闻

转型再出发 奋勇打头阵

房产要闻

475万人,猛增13.1%!国庆海南,又爆了?

旅游要闻

贵州新晋赏花天花板!娄山关四季花海,避暑遛娃一站式拿捏

军事要闻

直击南部战区海军某部重装空投训练

无障碍浏览 进入关怀版