彩信信令流程全解析:MM1到MM7时序、抓包与排查实战

发布时间:2026/10/4 13:20:03
彩信信令流程全解析:MM1到MM7时序、抓包与排查实战 简介这份PDF资料聚焦移动通信领域的彩信信令流程面向通信工程专业学生、运营商网络运维人员及移动增值业务开发者帮助读者系统理解彩信从发送到接收的完整信令交互机制。资源包内含1个PDF文件大小约1.06MB内容以图文结合方式梳理了终端到终端的多种业务流程包括立即取、超时转梦网相册以及非MMS终端等典型场景。资料详细拆解了发送方经WAP网关向MMSC提交彩信、MMSC通过短信中心通知收方、收方经PDP上下文激活取信等关键步骤并说明了MMSC重定向、归属MMSC判断、Push通知下发及梦网邮箱转存等信令细节。目前已有81人学习浏览适合需要掌握彩信业务底层信令走向、排查彩信收发异常或准备通信类技术面试的读者参考可帮助快速建立对MMSC、WAP网关、短信中心等网元协作关系的整体认知。1. 彩信信令流程.pdf 到底在讲什么从一条发不出去的彩信说起你给用户发了一条带图的彩信手机显示发送成功对方却迟迟收不到或者你在做短信网关对接MM7 接口返回Deliver状态码却卡在Expired。这类问题翻遍应用日志都找不到根因因为真正的答案藏在信令层——彩信信令流程.pdf这类资料讲的正是这条链路上每一跳的交互顺序、状态码和超时机制。它覆盖的是 MM1 到 MM7 各接口的请求响应时序、WAP 网关与 MMSC 的协作方式以及NotifyResp、RetrieveConf这些关键报文的触发条件。适合三类人做短信/彩信网关对接的后端工程师、排查终端收发异常的移动端开发、以及需要抓包定位运营商侧问题的运维。读完你能自己画出一次完整彩信投递的信令时序知道每个环节该看什么日志、抓什么包、调什么参数。2. 彩信信令流程的核心链路拆解MM1 到 MM7 各跳在干什么彩信不是一条短信那么简单它本质上是「通知 拉取」两段式架构。发送方把内容推到 MMSC多媒体消息服务中心MMSC 只给接收方发一条轻量的通知短信含 URL接收方终端主动发起 HTTP 拉取才拿到真正的内容。这个设计决定了信令流程天然分成「提交」和「投递」两条独立链路任何一条断了都会表现为「发送成功但收不到」。2.1 发送侧MM1 Submit 到 MM4 转发的完整时序发送侧从终端到 MMSC 走的是 MM1 接口协议栈通常是 WAP over HTTP。终端先和 WAP 网关建立连接然后 POST 一个M-Send.req到 MMSC 的提交地址。MMSC 收到后返回M-Send.conf里面带一个Message-ID这个 ID 是后续所有排查的锚点。MMSC 拿到消息后要判断接收方是不是本网用户。如果是异网就走 MM4 接口转发到对端 MMSC。MM4 用的是 SMTP 封装把彩信打包成 MIME 邮件投递。这一步常见的坑是 MM4 的Message-ID格式和 MM1 不一致跨网排查时对不上号。# 抓取 MM1 提交接口的 HTTP 报文终端侧或 WAP 网关侧 tcpdump -i any -A -s 0 tcp port 80 and host mmsc_ip -w mm1_submit.pcap # 用 tshark 过滤出 M-Send.req 和 M-Send.conf tshark -r mm1_submit.pcap -Y http.request.method POST -T fields -e http.request.uri -e http.response.code上面命令先全量抓包再过滤是因为 MM1 的 POST body 是二进制 WAP 编码直接看 ASCII 会乱码需要后续用 WAP 解码工具解析。-s 0保证不截断彩信 body 动辄几百 KB截断了就看不到完整内容。host换成你实际 MMSC 的地址端口按运营商实际配置常见是 80 或 8080。提交侧的关键参数有三个X-Mms-Message-Class区分个人消息和广告消息、X-Mms-Priority优先级影响队列调度、X-Mms-Delivery-Report是否要状态报告。很多「发送成功但无报告」的问题就是Delivery-Report没设对或者 MMSC 侧的报告回传开关没开。2.2 接收侧MM1 Notify 到 Retrieve 的拉取机制接收侧的信令更有意思。MMSC 确定接收方在线后发一条M-Notification.ind给终端这就是用户感知到的「您有一条彩信」短信。这条通知里带一个X-Mms-Content-Location是一个 URL。终端收到通知后先回M-NotifyResp.ind确认然后才发起 HTTP GET 去拉取内容。拉取走的是M-Retrieve.confMMSC 把彩信内容以 WAP 编码返回。终端拿到后回一个M-Acknowledge.ind整个投递才算完成。如果终端在通知后没有及时拉取MMSC 会重试通知重试次数和间隔由X-Mms-Number-of-Messages和运营商策略决定。# 解析 MM1 Notify 报文中的关键字段WAP 编码需先解码 # 常见做法是用 wappush 库或自研 WSP 解析器 from wappush import decode_notify raw_pdu bytes.fromhex(...) # 从抓包中提取的 Notify PDU notify decode_notify(raw_pdu) print(Content-Location:, notify.headers.get(X-Mms-Content-Location)) print(Message-ID:, notify.headers.get(X-Mms-Message-ID)) print(Expiry:, notify.headers.get(X-Mms-Expiry)) print(From:, notify.headers.get(From))这段代码的核心是拿到Content-Location和Message-ID。Content-Location是终端拉取内容的实际地址如果这个地址不可达比如 WAP 网关路由问题终端就永远拉不到内容。Expiry决定这条通知的有效期过期后 MMSC 不再重试用户就彻底收不到了。Message-ID用来和发送侧的 ID 做关联跨接口排查时这是唯一的串联线索。注意不同终端对Content-Location的处理有差异部分终端会强制走 WAP 网关代理如果网关配置了白名单而 MMSC 地址不在其中拉取就会静默失败。2.3 MM4 与 MM7 的差异跨网转发和 SP 接入的信令区别MM4 是 MMSC 之间的接口走 SMTP信令层面表现为邮件投递的MAIL FROM、RCPT TO、DATA三阶段。它的状态码是 SMTP 的 2xx/4xx/5xx和 MM1 的 WAP 状态码完全两套体系。跨网彩信收不到十有八九是 MM4 的RCPT TO被对端拒绝但本端只记录了「已转发」没记录对端的拒绝原因。MM7 是 SP服务提供商接入 MMSC 的接口走 SOAP over HTTP。SP 提交用SubmitReqMMSC 回SubmitRsp投递报告用DeliverReq。MM7 的状态码是1000表示成功2000系列表示客户端错误3000系列表示服务端错误。很多 SP 对接时只判断 HTTP 200 就认为成功忽略了 SOAP body 里的StatusCode导致消息实际被 MMSC 拒绝却浑然不知。接口协议典型状态码排查入口MM1WAP/HTTPM-Send.conf中的X-Mms-Response-Status终端日志、WAP 网关抓包MM4SMTP250/421/550MMSC 转发日志、对端 SMTP 日志MM7SOAP/HTTPStatusCode1000/2000/3000SP 侧 SOAP 日志、MMSC 接入日志这张表建议贴在工位上。排查时先确定问题出在哪条接口再去看对应的状态码不要一上来就翻应用日志方向错了查一天也查不出来。3. 用抓包和日志复现一次完整彩信投递从 Submit 到 Acknowledge理论讲完这一章直接上手复现。你需要准备三样东西一台能抓包的终端或网关镜像口、MMSC 的访问日志、以及一个能发彩信的测试号码。目标是把一次成功投递的完整信令序列抓下来形成你自己的排查基线。3.1 搭建最小抓包环境与关键过滤表达式抓包点选在 WAP 网关和 MMSC 之间的镜像口最理想能同时看到 MM1 的提交和投递。如果拿不到镜像口退而求其次在终端侧抓但只能看到 MM1 的终端侧交互看不到 MM4 转发。# 在网关侧抓取所有彩信相关流量按 MMSC IP 过滤 tcpdump -i eth1 -A -s 0 host 10.0.0.100 and (port 80 or port 25) -w mms_full.pcap # 实时观察 M-Send 和 M-Retrieve 的交互 tshark -i eth1 -Y http contains M-Send or http contains M-Retrieve -T fields -e frame.time -e ip.src -e ip.dst -e http.request.uri第一条命令把 MMSC 相关的 80 端口MM1/MM7和 25 端口MM4流量全抓下来-s 0不截断。第二条命令做实时过滤http contains匹配的是 HTTP body 里的 WAP 编码字符串虽然 body 是二进制但M-Send这类方法名在 PDU 里是明文 ASCII所以能匹配到。frame.time用来算各跳之间的时间差这个数据后面排查超时问题非常有用。抓包时要注意一个细节WAP 网关可能会对 MM1 流量做压缩或分片抓到的包不一定完整。如果发现M-Send.req的 body 不完整检查网关的 WSP 分片配置或者直接在 MMSC 侧抓入口流量做对比。3.2 逐跳解析 Submit、Notify、Retrieve、Acknowledge 报文抓到包后按时间顺序把四类报文找出来。下面是一个典型成功流程的时序时间戳是相对值T0.000 终端 - WAP网关 POST /mmsc M-Send.req (Message-ID: abc123) T0.150 WAP网关 - MMSC POST /mmsc M-Send.req (转发) T0.320 MMSC - WAP网关 HTTP 200 M-Send.conf (X-Mms-Response-Status: Ok) T0.350 WAP网关 - 终端 HTTP 200 M-Send.conf T1.200 MMSC - 接收终端 M-Notification.ind (Content-Location: http://...) T1.450 接收终端 - MMSC M-NotifyResp.ind (Status: Ok) T2.100 接收终端 - MMSC GET /mmsc/... M-Retrieve.conf T2.800 MMSC - 接收终端 HTTP 200 M-Retrieve.conf (内容) T2.950 接收终端 - MMSC M-Acknowledge.ind这个时序里有两个关键时间差T0.320到T1.200之间是 MMSC 的处理和路由时间正常在 1 秒内T1.200到T2.100是终端收到通知到发起拉取的间隔取决于终端策略有的终端会延迟几十秒。如果T2.100一直不出现说明终端没发起拉取问题在终端侧或通知没送达。# 从 pcap 中提取各跳时间戳计算关键时间差 import pyshark cap pyshark.FileCapture(mms_full.pcap, display_filterhttp) events [] for pkt in cap: if hasattr(pkt, http): method getattr(pkt.http, request_method, None) uri getattr(pkt.http, request_uri, None) ts float(pkt.sniff_timestamp) events.append((ts, method, uri)) # 找 M-Send 和 M-Retrieve 的时间差 send_ts next(ts for ts, m, u in events if m POST and u and mmsc in u) retrieve_ts next(ts for ts, m, u in events if m GET and u and mmsc in u) print(fSubmit 到 Retrieve 间隔: {retrieve_ts - send_ts:.3f}s)这段脚本用pyshark遍历 pcap提取所有 HTTP 请求的时间戳和方法。display_filterhttp只保留 HTTP 包减少内存占用。最后计算 Submit 到 Retrieve 的间隔这个值如果超过 60 秒基本可以判定投递链路有异常。sniff_timestamp是抓包时的绝对时间做相对计算前先减去第一个包的时间戳。3.3 用 Message-ID 串联全链路日志抓包只能看到网络层要定位应用层问题还得靠日志。核心思路是用Message-ID把 MM1、MM4、MM7 的日志串起来。MMSC 通常会在各接口日志里记录Message-ID但格式可能不同MM1 是abc123MM4 可能变成abc123mmsc.example.comMM7 又可能是纯数字 ID。# 在 MMSC 日志中按 Message-ID 搜索全链路记录 grep -r abc123 /var/log/mmsc/ | grep -E MM1|MM4|MM7 | sort -t -k1,2 # 如果 MM4 的 ID 格式不同先做格式转换再搜索 # 常见做法是提取 前的部分做模糊匹配 grep -rE abc123|abc123 /var/log/mmsc/ | sort第一条命令直接搜原始 ID适合 MM1 和 MM7 日志。第二条用正则同时匹配两种格式覆盖 MM4 的iddomain形式。sort -t -k1,2按时间排序方便看时序。如果日志量很大先按时间范围过滤再搜 ID否则 grep 会跑很久。提示部分 MMSC 的 MM4 日志不记录原始 Message-ID只记录 SMTP 的 Queue-ID。这种情况下需要先在 MMSC 的路由表里用 Message-ID 查出 Queue-ID再用 Queue-ID 去搜 MM4 日志。这个映射关系通常在 MMSC 的数据库或缓存里具体表名因厂商而异。4. 彩信信令排查避坑5 个让工程师熬夜的典型问题这一章全是血泪经验。彩信信令的问题有个特点现象和根因往往不在同一层应用层看到「发送成功」问题可能出在 WAP 网关的路由表终端看到「下载失败」根因可能是 MMSC 的Content-Location域名解析不了。下面 5 个坑按出现频率排序每个都按「现象 → 原因 → 解决」写。4.1 坑一M-Send.conf 返回 Ok 但消息从未投递现象发送侧收到X-Mms-Response-Status: OkMessage-ID也正常返回但接收方永远收不到通知MMSC 日志里找不到 MM4 转发记录。原因MMSC 的Ok只代表「已接收」不代表「已路由」。消息进入 MMSC 后要先做接收方查询通常是查 HLR 或内部路由表如果查询失败或超时消息会被静默丢弃但提交侧的响应已经发出去了。常见触发条件是接收方号码格式不对比如带了86而 MMSC 期望纯数字或者接收方是异网但 MM4 路由配置缺失。解决在 MMSC 的接收方查询日志里搜Message-ID看是否有RouteLookupFailed或HLRTimeout。如果是号码格式问题在提交侧做号码规范化统一去掉国际前缀。如果是 MM4 路由缺失补配置后重发。排查时不要只看提交侧日志一定要追到 MMSC 的路由模块。4.2 坑二Notify 发出但终端不拉取卡在 NotifyResp现象MMSC 日志显示M-Notification.ind已发送也收到了M-NotifyResp.ind但后续没有M-Retrieve.conf消息状态一直是Waiting。原因终端回了NotifyResp只代表「收到了通知」不代表「会立即拉取」。部分终端在弱网或省电模式下会延迟拉取延迟时间可能长达几分钟。更隐蔽的情况是Content-Location的 URL 用了内网地址终端在公网环境下解析不了拉取请求根本发不出去。解决先确认Content-Location是公网可达的域名或 IP。如果是内网地址检查 WAP 网关的代理配置确保终端能通过网关访问。如果是终端延迟拉取看 MMSC 的重试策略X-Mms-Expiry设得太短会导致重试次数不够。实测中把Expiry从默认的 7 天改成 1 天重试窗口反而更合理因为大部分终端会在 1 天内拉取。4.3 坑三MM4 跨网转发被对端 550 拒绝但本端无感知现象异网彩信发送失败本端 MMSC 日志显示「已转发」但接收方运营商侧查不到记录。本端 SMTP 日志里只有250 Queued没有后续状态。原因MM4 走 SMTP250 Queued只表示对端 SMTP 服务器接收了邮件不代表对端 MMSC 接受了彩信。对端可能在后续处理中返回550拒绝但这个拒绝是异步的本端如果没有配置 DSN投递状态通知就完全感知不到。解决在 MM4 配置里开启 DSN要求对端在投递失败时回传状态。同时在本端 MMSC 配置投递超时超过一定时间未收到对端确认就标记为失败并告警。排查时直接联系对端运营商查 SMTP 日志用Message-ID或Queue-ID定位。这个坑的教训是跨网问题单靠本端日志永远查不清必须建立端到端的追踪机制。4.4 坑四WAP 网关分片导致 M-Retrieve 内容不完整现象终端能拉取到彩信但图片显示不全或提示「文件损坏」。抓包看M-Retrieve.conf的 HTTP 响应是 200但 body 长度比预期短。原因WAP 网关对超过一定大小的响应会做分片segmentation终端需要按 WSP 的分片规则重组。如果终端的分片重组逻辑有 bug或者网关的分片大小和终端协商不一致就会导致内容截断。常见于大尺寸图片超过 100KB的彩信。解决在 WAP 网关侧调整分片大小或者关闭分片让终端一次性拉取。抓包时对比 MMSC 出口的Content-Length和终端收到的实际 body 长度差值就是丢失的部分。如果终端不支持大分片在 MMSC 侧做内容压缩把图片压到分片阈值以下。这个坑在 4G/5G 终端上已经少见但老款功能机仍然会遇到。4.5 坑五MM7 的 SOAP 状态码被 HTTP 200 掩盖现象SP 提交彩信后收到 HTTP 200以为成功但用户没收到。SP 侧日志只记录了 HTTP 状态码没解析 SOAP body。原因MM7 是 SOAP over HTTPHTTP 200 只代表「SOAP 请求被接收」真正的业务状态在 SOAP body 的StatusCode里。MMSC 可能在 HTTP 层返回 200但 SOAP 层返回2000客户端错误或3000服务端错误。SP 如果只判断 HTTP 状态码就会漏掉业务失败。解决在 SP 侧强制解析 SOAP body 的StatusCode非1000一律按失败处理并记录详细错误信息。常见错误码2001是号码格式错误2002是内容过大3001是 MMSC 内部错误。排查时把 SOAP body 完整打到日志里不要只打 HTTP 状态码。这个坑的通用教训是任何 SOAP/REST 接口业务状态码永远比传输层状态码重要。5. 把信令流程变成可复用的排查能力时序基线与自动化校验前面四章讲的是「怎么查」这一章讲「怎么让下次查得更快」。核心思路是建立你自己的信令时序基线然后用脚本做自动化校验把重复性的排查工作交给工具。5.1 建立你自己的时序基线表每次成功投递的抓包都是一条基线。把关键时间差记录下来形成一张基线表。下次出问题时对比实际时序和基线偏差大的环节就是嫌疑点。环节正常范围偏差大时的排查方向Submit 到 M-Send.conf 500msMMSC 入口队列积压、WAP 网关转发慢M-Send.conf 到 Notify 2sMMSC 路由查询慢、HLR 超时Notify 到 NotifyResp 1s终端侧问题、通知未送达NotifyResp 到 Retrieve 30s终端拉取策略、Content-Location 不可达Retrieve 到 Acknowledge 2s内容传输慢、分片重组问题这张表建议按你的实际环境填不同 MMSC 厂商和网络环境差异很大。填完后存成 CSV 或 JSON后面脚本直接读。# 自动化校验对比实际时序和基线输出偏差报告 import json baseline { submit_to_conf: 0.5, conf_to_notify: 2.0, notify_to_resp: 1.0, resp_to_retrieve: 30.0, retrieve_to_ack: 2.0 } def check_timing(actual): report [] for key, limit in baseline.items(): if key in actual and actual[key] limit: report.append(f{key}: {actual[key]:.3f}s 超过基线 {limit}s) return report if report else [所有环节正常] # 从抓包解析出的实际时序 actual_timing { submit_to_conf: 0.32, conf_to_notify: 0.88, notify_to_resp: 0.25, resp_to_retrieve: 0.65, retrieve_to_ack: 0.15 } for line in check_timing(actual_timing): print(line)这段脚本把基线定义成字典实际时序也定义成字典逐项对比。check_timing返回偏差列表空列表表示全部正常。实际使用时actual_timing应该从抓包脚本自动生成而不是手填。基线值要按你的环境调整比如内网环境submit_to_conf可以设到 200ms跨网环境可能要放宽到 1s。5.2 用脚本自动比对两次抓包的差异排查问题时经常需要对比「正常时」和「异常时」的抓包。人工对比太慢写个脚本自动 diff。# 对比两次抓包的报文序列差异 import pyshark def extract_sequence(pcap_file): cap pyshark.FileCapture(pcap_file, display_filterhttp) seq [] for pkt in cap: if hasattr(pkt, http): method getattr(pkt.http, request_method, RESPONSE) uri getattr(pkt.http, request_uri, ) seq.append(f{method} {uri}) return seq normal extract_sequence(normal.pcap) abnormal extract_sequence(abnormal.pcap) # 找出异常抓包中缺失的步骤 missing [step for step in normal if step not in abnormal] print(异常抓包中缺失的步骤:) for step in missing: print(f - {step})这个脚本提取两次抓包的 HTTP 方法 URI 序列然后找出异常抓包中缺失的步骤。display_filterhttp只保留 HTTP 包getattr带默认值防止属性不存在时报错。输出直接告诉你哪一步没发生比如正常流程有GET /mmsc/...而异常流程没有说明终端没发起拉取。这个脚本的局限是只对比 URI不对比 body对于状态码差异需要额外解析。5.3 一个我坚持了多年的习惯每次排查完一个彩信问题不管多简单我都会把抓包和日志存一份到案例库按「现象-根因-解决」命名。三年下来攒了上百个案例现在遇到新问题先搜案例库命中率超过一半。这个习惯的回报是复利式的第一次存要花 10 分钟但下次同类问题可能 1 分钟就定位了。另一个习惯是永远先看Message-ID的全链路日志再看抓包。日志告诉你「发生了什么」抓包告诉你「怎么发生的」顺序反了容易在细节里迷路。彩信信令的坑大多不在协议本身而在各厂商的实现差异和配置细节这些只能靠案例积累没有捷径。希望帮到你。本文还有配套的精品资源点击获取