ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

短信发送流程详解:从验证码提交到回执处理的全链路指南

短信发送流程详解:从验证码提交到回执处理的全链路指南 简介这是一份聚焦短信发送流程的PPT教案面向通信工程专业学生、核心网运维人员及网络优化工程师用图解方式讲解短信从发送方到接收方所经过的完整信令链路。内容按场景拆解为漫游用户MO流程、省内互通短信MO流程、省内用户MT流程、漫游用户MT流程、省外用户MT流程并清晰标注了BSC、MSC、LSTP、HSTP、SMSC、ISMG、HLR等网元在每一步中的职责与应答关系覆盖号段鉴权、位置查询、短信转发、计费话单生成等关键环节便于读者快速建立端到端流程的全局视图。压缩包共1个pptx文件大小158KB内容结构紧凑可直接用于课堂教学、自学入门或团队内部分享。目前已有67人学习下载适合作为理解短信网络架构的基础材料也能为后续排查短信发送失败、延迟等问题提供流程参考。1. 短信发送流程到底在送什么一次验证码的完整旅程运营群里有人喊“验证码 10 分钟没到”你查数据库看到状态是“提交成功等待回执”找通道方对线对方甩来一句“已提交运营商”。这条短信到底卡在哪一跳短信发送流程学习教案.pptx 这个标题讲的就是这条从业务系统到用户手机的完整链路验证码怎么被组装、怎么提交给网关、网关怎么转协议、运营商怎么下发、回执怎么回来。适合刚接手短信服务的后端开发、测试和运维也适合要跟通道方掰扯的运营。学完不是为了背字段而是能立刻回答三个问题消息停在哪、该不该重发、找谁要回执。2. 把短信发送流程拆成三段业务侧、网关侧、运营商侧2.1 短信发送流程里的三个角色与责任边界我见过太多人一上来就啃 SMPP 报文格式结果线上出故障时连“该找谁”都分不清。带这类教案我习惯先借用企业数据架构设计方法里的思路先画数据在哪产生、在哪流转、在哪消费再填细节。短信发送流程也一样三段边界必须在一开始就立住。业务侧是你的服务负责生成短信内容、校验签名模板、分配唯一消息 ID、记录发送状态。网关侧是通道方提供的接入服务负责协议转换、按号段路由、重发控制、回执回调。运营商侧是移动、联通、电信的短信中心负责真正的下行、计费、生成最终状态报告。故障发生时第一条判断准则就是这个状态是业务侧生成的还是网关回传的还是运营商回执里的。环节产生状态谁负责写库谁负责解释业务侧提交SUBMITTED业务系统本服务网关确认ACK / NACK网关通道方运营商下发SENT网关回传通道方手机侧结果DELIVRD / UNDELIV / EXPIRED运营商回执通道方这张表是整份教案的地基。确认和回执是两个不同的信号确认只代表“网关收了”不代表“手机收到了”。很多线上误判就是把 ACK 当成了成功。2.2 SMPP、CMPP 与 HTTP API三种接入方式怎么选教案里绕不开协议选型。实际从业方案里短信发送流程对接方式就三种SMPP、CMPP、HTTP API。不是越底层越好要看你的业务发往哪里。SMPP 是国际通用协议长连接、面向报文适合跨国业务和自建网关能承载大批量提交但接入成本高回执关联要自己处理。CMPP 是国内三大运营商行业网关常用的协议很多老系统还在跑字段里有 PK_TOTAL、PK_NUMBER 这类分包参数调试比 SMPP 绕。HTTP API 是聚合服务商最常见的接入方式提交一条 POST 请求同步返回消息 ID异步回调状态报告接入成本最低但排查问题时依赖对方日志。接入方式适用场景接入成本回执方式常见坑SMPP国际业务、批量高并发高长连接 deliver_sm需自己维护 session 和窗口CMPP国内运营商行业网关中网关主动推送分包、mt/ms 消息类型易混HTTP API验证码、通知、营销低回调或拉取回调丢失、签名校验如果只发国内验证码我建议先走 HTTP API如果业务线多、量级大再考虑 SMPP 自建接入。教案里不要让学生陷入“哪个协议更高级”的争论要看业务边界。3. 用本地仿真脚本跑通最小短信发送流程生产者、路由、回执3.1 零依赖的 Python 仿真从业务侧提交到手机侧回执只看协议不跑代码短信发送流程永远是黑匣子。我建议教案里加一个最小仿真脚本不依赖任何第三方库装完 Python 3.10 就能跑。它的意义不是模拟真实网关而是把状态变化顺序印在脑子里SUBMITTED → ACK → DELIVRD。# mock_sms_flow.py # 最小短信发送流程仿真业务侧提交、网关路由、运营商回执 import time import uuid from datetime import datetime def submit_sms(phone: str, content: str): 业务侧提交消息分配 msgid 并写入初始状态 msgid uuid.uuid4().hex[:16] print(f[SUBMIT] msgid{msgid} phone{phone} time{datetime.now()}) return { msgid: msgid, phone: phone, content: content, status: SUBMITTED, channel: , } def route_sms(msg): 网关侧按号段路由这里用 mock 通道代替真实 CMPP/SMPP 连接 if msg[phone].startswith(138): msg[channel] cmpp-mock else: msg[channel] smpp-mock msg[status] ACK print(f[ROUTE] msgid{msg[msgid]} channel{msg[channel]} status{msg[status]}) return msg def deliver_report(msg, delay: float 1): 模拟运营商回执默认投递成功 DELIVRD time.sleep(delay) msg[status] DELIVRD print(f[REPORT] msgid{msg[msgid]} statDELIVRD done_time{datetime.now()}) return msg if __name__ __main__: msg submit_sms(13800009999, 验证码1234565分钟内有效。) msg route_sms(msg) msg deliver_report(msg) print(f[FINAL] phone{msg[phone]} status{msg[status]})保存为 mock_sms_flow.py在终端执行python3 mock_sms_flow.py。你会看到三条带时间戳的日志顺序与真实发送流程一致。逻辑上submit_sms 负责创造消息并分配 msgid这一步对应真实系统里写发送记录route_sms 根据号段把消息扔给不同通道对应网关侧的路由表deliver_report 延迟一秒后把状态改成 DELIVRD对应运营商回执。参数说明里最值得关注的是 delay。它模拟的是“运营商下发给手机”这段网络时间真实环境里通常 3 到 30 秒。如果 delay 超过你的超时阈值教案里就该引出一个问题这条消息算失败吗答案是不算只能继续等回执超时重发要按幂等规则来。3.2 状态机表格与异步化改造建议仿真脚本把发送流程压缩成了同步函数真实系统里每一步都是异步的。教案里对应放一张状态机表比堆协议字段有用得多。状态产生方含义下一步动作SUBMITTED业务系统已生成消息尚未确认等待网关 ACKACK网关网关已接收不代表送达等待状态报告SENT网关已送运营商短信中心继续等待最终回执DELIVRD运营商手机已收到流程结束UNDELIV运营商投递失败查原因决定是否重发EXPIRED运营商超过有效期未下发终止不重发REJECTD网关被拒绝如签名或模板不合法修正后重发真实系统里SUBMITTED 之后的消息通常要进消息队列用 Kafka 或 Redis 做削峰。网关侧提交后回调线程按 msgid 找到对应记录更新状态回执线程再按 msgid 更新最终结果。教案里抛一个问题给学员如果 ACK 还没回来业务服务重启了这条消息应该怎么处理从业方案是落一张发送流水表以 msgid 为唯一键重启后从未确认状态捞起来重新查询网关而不是直接重发。4. 状态报告与重试把回执字段读对短信发送流程才算闭环4.1 回执字段到底怎么解析DELIVRD 不是唯一答案短信发送流程中最容易翻车的环节是回执解析。很多新手看到 stat 字段等于 DELIVRD 就认为成功看到 UNDELIV 就重发完全忽略 done_time、err_code 和 submit_date。回执要和原始提交记录按 msgid 关联先查流水再改状态否则回执晚到几分钟就会把成功记录覆盖成失败。# parse_report.py # 回执解析按 msgid 关联发送记录区分最终状态和中间状态 REPORT_FINAL_STATES {DELIVRD, UNDELIV, EXPIRED, REJECTD} def handle_report(report: dict, send_records: dict): report 包含 msgid/stat/done_timesend_records 是发送流水表 record send_records.get(report[msgid]) if record is None: print(f[ORPHAN] msgid{report[msgid]} 找不到对应提交记录) return stat report.get(stat, ) print(f[REPORT] msgid{report[msgid]} old{record[status]} new{stat} done{report.get(done_time)}) # 只做最终状态覆盖避免 ACK/SENT 中间状态把结果改脏 if stat in REPORT_FINAL_STATES: record[status] stat if stat ! DELIVRD: print(f[ALERT] msgid{report[msgid]} 失败原因 err_code{report.get(err_code)})这里的关键是按 msgid 找到原始记录这一行。真实通道回执有两个最常见的坑一是回执里的 msgid 是你提交时返回的不是你自己生成的 UUID需要建立映射二是回执可能乱序DELIVRD 先到、SENT 后到所以只允许最终状态覆盖最终状态。代码里的 REPORT_FINAL_STATES 就是用来拦截中间状态的过滤器没有它晚到的 SENT 会把 DELIVRD 覆盖成“已发送”线上就会出“明明成功却显示发送中”的幻觉。4.2 重试退避与幂等三个必调参数短信重发是最容易引发客诉的操作。用户收不到验证码业务侧第一反应是重发结果网关回执只是延迟用户最后收到两三条相同验证码。教案里要明确重试只针对“明确知道网关没收到的场景”例如 ACK 超时或 NACK收到 UNDELIV、EXPIRED 这种运营商回执后不要自动重发转人工或者走语音验证码兜底。# retry_policy.py # 短信重试策略指数退避 幂等保护 RETRY_CONFIG { max_retry: 3, base_interval_sec: 5, timeout_sec: 30, power: 2, } def should_retry(status: str, retry_count: int) - bool: 只有 ACK 超时才重试明确的运营商失败状态不重试 if status in (UNDELIV, EXPIRED, REJECTD): return False if retry_count RETRY_CONFIG[max_retry]: return False return True def next_interval(retry_count: int) - int: 退避间隔5s、10s、20s指数递增 return RETRY_CONFIG[base_interval_sec] * (RETRY_CONFIG[power] ** retry_count)参数上最值得调整的是 timeout_sec。国内验证码通道业内常见阈值是 30 到 60 秒如果设成 5 秒基本每次都会误判超时然后重发。base_interval_sec 建议从 5 秒起不要用固定 1 秒去轰炸网关。max_retry 超过 3 次意义不大因为第 4 次重发时用户已经去点“获取语音验证码”了。幂等部分必须落到数据库唯一索引上。从业方案里发送流水表以 request_id 为唯一键同一业务请求重复提交时直接返回已有 msgid网关侧再配合业务侧的流水号去重才能扛住双重重试。只靠 Redis 分布式锁不够锁过期后第二次请求还是会进来。5. 短信发送流程的避坑清单从乱码到到达率玄学的 5 个真实场景5.1 短信内容乱码签名被截断现象用户收到的短信出现火星文或者签名“【XX银行】”消失。原因网关侧按 GSM-7 编码解析你传的 UTF-8 字符串中文字符被拆错另一种情况是短信超过 70 个字符被按长短信拆分签名拼接位置不对被截断。解决统一在提交参数里指定编码为 UCS-2中文内容按 70 字一计费单位长短信用网关的拼接能力签名放在内容开头而不是结尾并且不要把签名算进变量长度里。5.2 状态显示 DELIVRD用户硬说没收到现象发送记录里 statDELIVRD用户投诉没收到。原因DELIVRD 只代表运营商短信中心下发给手机成功不代表用户看到了。手机关机后开机补发、短信被手机系统拦截、伪基站干扰都会造成这种“假成功”。解决别急着反驳用户先按手机号找通道方要运营商侧的下发详情确认短信中心下发时间和终端状态同时让用户检查拦截列表。教案里要强调回执是最终参考不是唯一真相。5.3 ACK 超时重发用户收到两条验证码现象同一验证码短时间内收到两条短信时间间隔 1 分钟左右。原因业务侧设置的超时时间太短网关实际已接收但 ACK 回传延迟业务侧判断超时后重发。解决把幂等键落到 request_id重发前先查同一 request_id 的提交记录超时后先调网关查询接口确认消息状态不要直接重发。这条在教案里应该单独作为反面案例比讲十页协议格式都管用。5.4 测试环境一切正常生产环境全部拒收现象同一套代码测试通道跑得通切到生产通道后短信全部被拦截或回执 REJECTD。原因生产签名或模板没有报备报备的签名与提交的签名不一致变量内容与模板不匹配。解决把签名、模板 ID、变量内容拆成三个字段管理提交前做本地校验测试环境也要用生产同款签名做“影子报备”不要让开发裸奔到上线才暴露。很多公司把这条归为配置问题实际是流程缺失。5.5 回执永远不来全部卡在 ACK现象发送后网关 ACK 正常但状态报告一直不回调流水全停在 ACK。原因提交时没有开启回执开关或者回调地址配错、回调验签失败被网关丢弃。解决提交参数里把 registered_delivery 置为 1检查回调接口是否返回 200验签失败时要能看到日志而不是静默丢弃。这个坑我见过多次多数是网关文档里叫回执标志开发漏传了。现象首选排查位置误判代价乱码截断编码参数与签名位置用户看不懂短信DELIVRD 但没收到运营商下发日志客诉升级重复验证码幂等键与超时配置资金安全风险生产拒收签名模板报备上线失败卡在 ACK回执开关与回调日志功能不可用6. 用教案做验收从链路图到消息追踪练习6.1 第一课先画链路图再谈协议我带人学短信发送流程时第一个动作不是看 pptx 里的报文格式而是让他在白板上画出六个节点业务系统 → 发送流水 → 网关接口 → 运营商短信中心 → 用户手机 → 状态报告回流。每个节点写清楚状态字段和超时阈值画完这张图协议字段才有位置安放。教案里应该把这张链路图作为课前作业而不是教学内容。6.2 必考题三分钟定位一条丢了的短信最后一个练习我会出一道必考题用户在晚上 23:00 说没收到验证码给你 msgid三分钟内说出定位步骤。正确顺序是第一步查流水确认状态停在 SUBMITTED 还是 ACK第二步查网关日志确认 ACK 有没有回来第三步调通道方查询接口拿到运营商回执第四步看手机是否正常。每一步都有明确的负责人和后手而不是一句“帮我查一下”。这条链路走一遍教案的内容才算真正闭环。我现在的习惯是任何短信接入项目先备好语音验证码兜底再把回执解析和幂等重试写成单元测试最后才调 UI。希望帮到你。本文还有配套的精品资源点击获取
返回列表