
简介中国移动通信USSD应用接口协议是中国移动通信集团发布的企业标准文件属于USSD系列标准中的核心文档面向通信领域从事业务规划、设备选型及网络运维的工程师。该标准围绕900/1800MHz TDMA数字蜂窝移动通信网完整规定了USSD支持的业务类型、组网方式、业务流程、编号方式、应用系统接口、计费与信息安全等技术要求并给出USSD应用接口协议在API、HLR、MAP、MSISDN等方面的关键实现细节。USSD作为一种操作简便、扩展性强的补充业务其响应速度与交互能力优于普通短信文档中对这一业务特点及适用场景也有清晰说明。资源包内仅含一个doc格式文档大小781KB但内容组织非常系统涵盖业务总体技术要求、应用接口协议、设备规范、标准编号等多个章节并梳理了QB-D-069-2009、QB-D-070-2009等系列标准编号体系便于按需查阅或系统学习。目前已有576人学习下载是一份权威、实用的行业规范参考资料适合项目研发、网络运行维护及标准研究等场景使用。1. 为什么还要研究中国移动通信USSD应用接口协议用户在地铁里拨“*100#”两秒内弹出余额银行柜员在自助终端上按“*21*卡号#”查自己的一笔记账政务大厅的排队机用同一串号码拉起办事进度——这些场景背后不是短信也不是 App而是一条仍然存活在移动网络深处的补充业务通道。中国移动通信USSD应用接口协议就是中国移动把这条通道对外开放给行业应用时定义的一套对接约定业务侧通过网关接入接收用户拨号请求、返回文本菜单、维护多轮会话把 USSD 变成一种不用安装、秒级响应、按会话联动的业务通道。本文适合正在接入充值查询、营销互动、企业自服务的 SP、系统集成商和平台工程师读。USSD 技术确实很老但它的触达率至今是短信之外最能打的方案之一。2. USSD 的协议栈与会话模型它和短信、IVR 到底差在哪2.1 USSD 与短信、IVR 的定位差一条消息还是一段对话USSD 的英文全称是 Unstructured Supplementary Service Data直译是“非结构化补充业务数据”。它从 GSM 时代就存在本质是移动网络里的一条信令业务通道。要理解中国移动开放它的理由必须先分清它和短信的本质区别短信是无状态、存储转发的发出去之后业务侧只能等回调用户侧看到的是异步消息而 USSD 是有状态、会话式的从用户拨号开始到业务结束网络侧会为一个会话保持上下文每一步请求和响应都在同一条链路上完成。这个差别在“查询余额”这种场景里非常直观。短信查询要先发指令、等系统处理、再收一条回执中间有 3 到 10 秒的延迟而且用户要自己区分哪条是回复USSD 拨号之后 1 到 2 秒就能弹出菜单或结果因为整个会话是同步的。IVR 则是另一条路线它需要占用语音通道用户要听菜单、按键体验上比 USSD 更慢而且在大音量环境下基本不可用。USSD 是纯文本、静默的适合在会议室、地铁、嘈杂工厂里操作。维度短信IVRUSSD会话状态无状态异步有状态语音通道有状态信令通道典型时延3~10 秒10 秒以上1~2 秒展示方式消息列表语音播报手机屏幕文本用户成本按条计费或套餐占用通话时长通常免费交互深度单轮为主多轮菜单多轮菜单所以我在给客户选型时有一条很朴素的经验通知类业务走短信复杂导航走 IVR 或 App而“用户主动发起、希望马上拿到结果、又不想装 App”的业务优先考虑 USSD。中国移动到现在仍保留 USSD 能力不是因为它先进而是因为它在特定场景下成本最低、触达最稳。2.2 信令面与应用面的分层对外接口协议到底在哪一层很多第一次接触 USSD 的开发者会去翻 3GPP 文档然后被 MAP、TCAP、SCCP 这一串缩写劝退。这里需要先建立一个分层认知手机拨号到网络侧处理走的是移动网信令面而行业应用接入 USSD 业务走的通常不是信令面而是中国移动对外提供的 USSD 应用网关。你对接的是网关的 TCP 接口不是核心网元。信令面的底子是七号信令。手机通过 BSS/RAN 接入后MSC/VLR 收到用户的 USSD 请求把这个请求封装成 MAP 操作送给 HLR 或者专门的 USSDCUSSD CenterUSSD 中心。3GPP 在 TS 22.090 定义业务需求TS 23.090 定义网络功能TS 24.090 定义协议细节核心的 MAP 操作是 UnstructuredSS-Request 和 UnstructuredSS-Notify分别对应移动台发起请求和网络侧主动下发通知。对行业应用来说你不需要解析这些信令也不需要关心 TCAP 的事务编号怎么配对那些是移动核心网工程师的事。应用面和信令面之间有一层网关做转换移动侧网元收到 MAP 请求后把它转换成一个应用层报文通过 TCP 长连接推到 SP 的服务器SP 返回的内容再由网关封装回 MAP 响应。整个链路我做对接时只看四样东西报文头格式、会话标识怎么关联、USSD 字符串怎么编码、超时和重发规则。下面这张表是我理解的协议栈分工也建议你按这个粒度去问移动侧要文档。层级承担者典型协议/标准业务侧要关心吗无线与交换BSS/RAN、MSC/VLRGSM/LTE 信令不需要事务与寻址SCCP、TCAP七号信令不需要USSD 业务操作HLR/USSDC3GPP TS 23.090/24.090了解即可应用接入USSD 应用网关中国移动应用接口协议核心业务实现SP/企业服务器自研业务逻辑核心如果移动侧给你的文档里出现了大量“原语”“对话端口”这类词说明这是给核心网看的真正的应用接口文档应该告诉你连接地址、端口、报文格式和字段枚举而不是 TCAP 的编码规则。2.3 会话状态机与关键字段会话 ID、USSD String、DCS 一次说清USSD 的会话模型和一个 HTTP 长连接有点像但更简单。一次完整会话分成三个阶段建立、交互、释放。建立阶段由用户拨号触发移动网元给这个会话分配一个唯一标识交互阶段是业务侧和用户之间往返多条菜单释放阶段由用户挂断、超时或业务侧主动结束。会话状态机里最容易被业务开发忽略的是“网络侧可以中途释放”。手机信号波动、用户拨了另一个号码、网关定时器超时都会让会话提前终止。业务侧如果还在等用户下一轮输入必须有一个超时机制把本地状态清掉否则内存里会堆一堆僵尸会话。我在设计时会区分两个超时网关侧的会话超时和业务侧的应用超时应用超时要设置得比网关短这样至少能保证先由业务侧收尾。关键字段里第一个是会话 ID。报文里每个请求和响应都必须带同一个会话 ID多轮菜单就靠它串起来网关靠它做路由业务侧靠它在本地内存或 Redis 里找上下文。第二个是 USSD String。它是用户拨号串的主体比如“*100#”的服务代码部分是 100参数可以跟在“*”后面*100*张三#。第三个是 DCSData Coding Scheme数据编码方案它决定 USSD 字符串是 GSM 7-bit 还是 UCS2中文场景必须处理具体在第 4 章展开。还有一个字段容易被忽略TON/NPI也就是地址类型和编号计划。它告诉网络侧“*100#”是服务代码还是普通号码。对接时如果发现自己发出去的请求被当成呼叫而不是 USSD多半是这个参数没配对。中国移动的接入文档里通常会给明确的取值不要自己猜猜错的结果就是用户那边完全没反应。3. 在中国移动网络里跑通 USSD 应用接口报文字段与会话流程3.1 接入前置条件拿服务代码、开测试号段、定连接方式在写任何代码之前先要有接入资格。中国移动对 USSD 开放接入不是随意申请的一般需要走运营商业务接入流程提交企业资质、说明业务场景、申请一个或一组 USSD 服务代码类似短信服务代码的申请逻辑同时申请一批测试用户号码用于联调验证。服务代码的位数和号段范围各个时期不一样常见的是 100 附近的短号码或者带“*”的扩展码你负责对接时以移动侧下发的正式通知为准。连接方式上行业应用通常通过 USSD 应用网关接入。网关侧会分配一个接入 IP、端口、以及用于鉴权的账号密码或密钥。连接类型以 TCP 长连接为主业务侧作为客户端主动发起连接网关负责把业务响应回传给用户。少数场景会要求 SP 侧开放端口让网关回连但这种模式在网络隔离上比较麻烦现在用得少。我一般会在 DMZ 区放一台接入机只放通到网关的 IP:端口业务数据库绝不放在同一台机器上。接入前还要确认两件事第一测试号段能否收发 USSD不是所有号码都开了 USSD 权限部分省对预付费卡有限制第二联调窗口期通常运营商侧会给出一个测试时间段而不是 7×24 小时随时可测。我踩过一次坑拿普通办公卡拨号一直没反应最后换了测试卡立刻通了问题就在号段权限上。3.2 报文最小字段集设计一次查询请求够用但不冗余应用接口协议的核心是报文格式。不同省份、不同时期的中国移动 USSD 网关报文格式会有差异但字段骨架大体一致。下面这张表是一个可落地的报文头设计字段顺序、字节长度请以你拿到的协议文档为准这里展示的是我在实际对接中常用的风格。字段长度必填说明消息类型1是request / response / notify / release总长度4是报文总字节数含头序列号4是会话内递增用于重发去重时间戳16是YYYYMMDDHHMMSS防重放会话 ID16是多轮会话关联的唯一标识主叫号码21是用户 MSISDN带国家码被叫服务码21是用户拨号的服务代码部分DCS 编码1是0x00 GSM 7-bit / 0x08 UCS2语言2否zh / en用于菜单文案切换报文体N是USSD 字符串内容序列号和会话 ID 的职责要分清。会话 ID 是跨多轮会话保持不变的序列号是同一会话内每个消息递增的。网关靠“会话 ID 序列号”判断是重复消息还是新消息业务侧重发时如果序列号不对网关可能把它当成新请求或直接丢弃。字段设计的原则是“够用但不冗余”不要把业务内部 ID 都塞进报文里网关只关心上面这些塞多了反而容易在转换 MAP 消息时被截断。一个典型的查询请求报文在协议文档里通常长这样这是对接三层网关时的常见表示不是中国移动某份合同原文请求: OPrequest SESSION8A7F0012 DCS0x00 SEQ0001 TS20250510143000SRC8613812345678 DST100 USSD*100#响应: OPresponse SESSION8A7F0012 DCS0x08 SEQ0002USSD1.余额查询\n2.流量查询\n3.套餐办理\n回复序号继续00返回参数说明SRC 是用户号码DST 是服务代码USSD 是用户实际拨号的字符串DCS 表示响应里的中文必须用 UCS2 编码。业务侧收到请求后的首要任务是校验 SRC 号码归属地、DCS 是否支持、然后按业务逻辑返回文本菜单。很多新手会把重点放在菜单 UI 上实际上第一要务是正确解析字段并回包菜单是后面的事。3.3 会话时序从用户拨号到菜单返回的完整链路把时序写清楚对接时才能定位问题出在哪一段。一次典型的中国移动 USSD 查余额会话分七个步骤用户手机拨号例如“*100#”或“*100*13812345678#”。MSC/VLR 把请求封装成 MAP UnstructuredSS-Request路由到 USSDC。USSDC 识别出服务代码查应用路由表把请求转成应用报文推给 SP 接入服务。SP 解析报文校验会话调用内部查询接口取数。SP 组织响应报文带同一会话 ID把菜单或结果文本回给 USSDC。USSDC 将响应封装成 MAP 响应送回手机手机屏幕弹出菜单。用户选择某一项重复步骤 1 到 6全部结束后任意一方发出 release 消息会话关闭。这里请特别注意第 5 步和第 7 步。业务处理完后如果只是把结果文本回给用户而不主动释放会话会话会一直挂在网关上直到超时。中国移动网络侧通常有定时器常见的是几十秒到两分钟不等但你不能赌它一定断。我一般会在最后一条菜单的 USSD 字符串里明确告诉用户“回复 00 结束”同时业务侧在用户选择结束后主动发 release保证会话第一时间关闭释放网关资源。菜单分页是另一个容易出问题的地方。USSD 字符串在网络侧有字节长度上限常见的限制是 160 字节以内这个限制在 3GPP TS 24.090 里能查到依据。GSM 7-bit 编码下约等于 160 个字符看起来很多但中文用 UCS2 编码后每个汉字占 2 字节实际只能放大约 80 个汉字。如果业务菜单超过这个长度必须分页展示不要试图一次塞进去。我设计菜单时有一条硬规则GSM 7-bit 单页控制在 140 个字符以内UCS2 单页控制在 70 个汉字以内留出 10% 余量给终端的显示截断和符号拼接。3.4 心跳与重连长连接网关保活是基本功USSD 网关通常要求 SP 侧维持长期 TCP 连接连接断开后网关会把业务路由临时摘除新请求可能直接失败。建立保活机制是接入的基本功心跳报文和业务报文共用同一报文头消息类型固定为心跳报文体为空。中国移动侧对心跳周期的要求一般在 30 秒到 5 分钟之间以接入文档为准。我常用的心跳策略是双保险业务侧每 30 秒发一次心跳同时监听链路的空闲时间如果超过 90 秒没有任何心跳响应或业务响应主动断开重连。重连时要做指数退避第一次等 1 秒第二次 2 秒最多退到 30 秒避免网关一恢复就被一群客户端同时重连打垮。这个“重连风暴”在多方接入的公网环境里遇到过两次网关侧一挂所有 SP 同时重连网关起来又被冲垮最后要靠错峰重连解决。4. 中国移动 USSD 接入避坑指南5 个高频翻车点与对应解法4.1 会话超时用户卡在“请稍候”半路状态清不掉现象用户拨号后能看到第一条菜单等了几秒没操作再输入序号就没反应了或者用户回了一条业务侧却显示会话已不存在。原因USSD 会话是有生命周期的网络侧定时器到了就释放。定时器长度由移动网元和接入协议约定有些场景只有 30 秒到 90 秒。业务侧没有独立的更短的会话等待定时器导致网关释放了会话业务侧还傻等。另一个常见原因是应用服务器处理慢数据库查询超过 5 秒整个交互节奏被拖垮。解决业务侧会话超时必须比网络侧短我一般设置为 20 秒到 25 秒无用户输入就主动发 release 并清理本地状态。取数类接口要控制在 1 秒内返回超过就直接提示“系统繁忙请稍后再试”。同时日志里把“会话超时”和“用户主动退出”分开记录否则事后统计用户流失率会失真。实际对接时还有一个细节中国移动不同省网的定时器不完全一样联调阶段要拿真实测试卡逐项验证不要假设全国统一。上线前把会话超时时长做成配置项方便战场调参。4.2 中文菜单乱码DCS 编码和字节数对不上现象用户手机弹出来的菜单是一串“锟斤拷”或者只有前半段正常、后半段截断甚至整个菜单直接消失。原因USSD 响应用错了 DCS。GSM 7-bit 字库里没有中文中文必须用 UCS2 编码同时把 DCS 置为 0x08。如果业务侧返回的是 GB2312 或 UTF-8 字节流DCS 却标了 0x00网关按 7-bit 解出来就是乱码。还有一个隐蔽原因DCS 设对了但字节数算错报文体长度按 ASCII 字符数算中文按一个字节算实际 UCS2 下每个字占两字节导致报文总长度小于真实长度网关读消息读到一半就断了。解决响应中文菜单时统一走 UCS2代码里显式转换不能用系统的默认字符集。字符串长度计算用字节数而不是字符数像 Java 里 getBytes(UTF-16BE).lengthPython 里 encode(utf-16-be) 后的长度。联调时先用手动构造的固定报文验证 DCS 0x08 能正确显示再接入业务系统。另外中文菜单的结束符“#”也要按 UCS2 编码整个 USSD 字符串保持一致编码不要前半段 GSM 7-bit、后半段 UCS2 混着来网关一混合解码就翻车。4.3 GSM 7-bit 位序反转看起来是对的解出来是反的现象英文和数字菜单在部分手机上乱码但是另一部分手机正常或者短信测试正常USSD 测试不正常。原因GSM 7-bit 的打包不是简单地把每个字符的 ASCII 码按字节拼接而是把每个字符的 7 个 bit 按从低位到高位的顺序写进位流再按 8 位一组切成字节。很多开发者图省事直接把“*100#”的 ASCII 字节塞进报文网关端用标准 GSM 7-bit 解包解出来自然是对不上的。反过来也一样你按标准算法打包网关按 ASCII 直读一样错。解决编解码必须用同一套标准算法不要自己造。写代码时优先找运营商给的工具或库没有库就严格按 3GPP TS 23.038 的 packing 规则实现并用已知的测试向量验证。我习惯在联调前先用一组固定输入做自测比如字符串“123”打包成标准 GSM 7-bit再用对应的解码器解回来如果解回来不是“123”说明实现有误别等网关测。这里需要提醒部分 USSD 网关在应用接口层直接用 ASCII 字符串而不是 GSM 7-bit 打包后的字节流也就是说编解码的坑可能只在网关内部SP 侧做的是明文传输。判断标准很简单看协议文档里报文体字段是“USSD String 明文”还是“GSM 7-bit 编码后的字节”。文档没写清就找对接人确认这是典型的“黑匣子”值得花十分钟问清楚省得后面血泪排错。4.4 服务代码里的“#”被网关当成结束符参数全丢了现象用户拨“*100*45025#”这种带参数的服务代码业务侧收到却只有“*100”参数部分没了有的网关更极端连“#”后面的响应都会异常。原因在 USSD 协议里“*”是参数分隔符“#”是用户拨号的结束符。到了应用接口层网关已经做了协议解析理论上“#”不会再传给 SP但如果会话里出现二次拨号、或者用户在某些终端上把“#”当普通字符输入网关的解析规则和 SP 侧预想的不一致就会丢。解决第一不要在 USSD 字符串里依赖“#”后面的内容传业务参数参数一律用“*”分隔例如“*100*1*45025#”。第二联调时明确问对接人应用层报文里的 USSD 字段是否带“#”带和不带是两种解析逻辑。第三当业务侧需要构造新菜单继续会话时不要自己拼“#”让网关根据当前会话状态处理你的职责是返回菜单文本而不是重新构造拨号串。当年第一次对接时我把“#”按普通字符处理去匹配服务代码结果正式环境有 30% 的请求匹配不上查了一天发现是省网网关在转发时把“#”剥掉了从此我把服务代码解析写成“剥离 # 后匹配”。4.5 重发报文被当成新会话幂等设计缺失现象用户手机网络抖动USSD 请求重发了业务侧处理了两次查余额的接口调了两次扣费类业务直接扣了两次钱。原因USSD 会话在信令面有重发机制网关会按一定策略重发应用报文。如果业务侧没有做幂等单纯按收到一次请求就处理一次就会重复。特别是充值、订购这类写操作后果很直接。解决用“会话 ID 序列号”做幂等键。第一次收到时处理并缓存处理结果同样的会话 ID 和序列号再次到达时直接返回缓存或确认不再触发业务逻辑。序列号不同的消息才代表新的一轮交互。还要注意用户在一个会话里多次选择菜单也是同一个会话 ID但序列号不同不能把整会话去重只能按“会话 ID 序列号”这个粒度去重。上线前建议做一次模拟重发测试把同一条请求报文连续发两次检查业务侧是否只执行一次。这个测试成本很低比出了事故再补救便宜得多。5. 上线前的拨测验证方法用 ATCUSD 和测试卡把端到端路径跑一遍验收到位的关键是模拟真实手机行为。普通手机手工拨号验证只能确认“能不能通”做不了自动化回归。我一般用支持 AT 命令的测试终端或 UE 模拟器发送标准的 ATCUSD 命令ATCUSD1,*100#,15其中 1 表示发起 USSD 会话请求15 表示 GSM 7-bit 默认字母表。如果联调中文菜单部分模组会把 15 换成 72 表示 UCS2具体看模组厂商文档。命令发出后模组会返回 ATCUSD0, 菜单内容, 15。第一个参数 0 表示网络侧已响应1 表示会话还在继续需要用户进一步输入2 表示网络侧已释放会话。我在回归脚本里会把三种返回分别断言0 检查内容1 检查后续可交互2 检查会话清理。配合测试号码段可以做到每轮发版后十分钟内跑完全量菜单路径不用人工一页页点。拨测之外我强烈建议保留两条生产习惯。第一任何 USSD 字符串改动先在本地用 DCS 0x00 和 0x08 两种编码各编解码一次确认长度和字符集正常再发布这能提前挡住第 4 章里一半的坑第二压测不要直接打正式业务库USSD 网关的并发会放大到后端先接测试环境跑 200 并发会话观察会话超时率和错误率再逐步放量。最后检查日志里有没有大量“会话不存在”和“重复请求”这两类日志是协议对接质量最直接的体检指标。我自己的教训是USSD 接入不怕报文复杂怕的是字段含义没对齐就着急联调。先花半小时把会话 ID、DCS、序列号、心跳周期这四个字段和对接人逐字确认清楚后面能少加两周班。希望帮到你。本文还有配套的精品资源点击获取