当前位置: 首页 > news >正文

SMPP协议解析

本文详细介绍了SMPP协议,一种在移动通信网络中发送和接收短信的二进制协议,涉及其基本特性、消息类型、流程、安全性和应用领域,重点讲解了Bind、Submit、Deliver等关键操作。

SMPP(Short Message Peer-to-Peer)是一种用于发送和接收短信消息的协议,通常用于移动通信网络。它允许应用程序通过短信中心(Short Message Service Center,SMSC)发送和接收短信。下面是对SMPP协议的详细解释。

SMPP(Short Message Peer-to-Peer Protocol)是用于短信服务的高效协议,解析其协议数据单元(PDU)需掌握以下核心要点:


1. SMPP协议基础
• 用途:连接短信网关(ESME)与短信中心(SMSC),支持短信提交、投递及状态查询。

• 版本:常见版本3.3、3.4、5.0,不同版本字段可能存在差异。

• 通信模式:基于TCP/IP,使用大端字节序(网络字节序)。


2. PDU结构解析
每个PDU包含Header(固定16字节)和Body(可变长度):

Header结构

struct {uint32 command_length;   // 整个PDU长度(含Header和Body)uint32 command_id;       // 命令类型(如submit_sm=0x00000004)uint32 command_status;   // 响应状态(0表示成功)uint32 sequence_number;  // 序列号(用于请求-响应匹配)
}

3. Body字段解析示例:submit_sm

# 字段顺序及类型(以SMPP 3.4为例)
service_type        : COctetString(1-6)    # 服务类型(如空字符串)
source_addr_ton     : uint8               # 源地址类型(如国际号码=0x01)
source_addr_npi     : uint8               # 源编号计划(如ISDN=0x01)
source_addr         : COctetString(1-21)  # 源地址(如手机号)
dest_addr_ton       : uint8               # 目标地址类型
dest_addr_npi       : uint8               # 目标编号计划
destination_addr    : COctetString(1-21)  # 目标地址
esm_class           : uint8               # 消息类型(如普通消息=0x00)
protocol_id         : uint8               # 协议标识(通常0x00)
priority_flag       : uint8               # 优先级(0-3)
schedule_delivery_time: COctetString(0-17) # 定时发送时间(可选)
validity_period     : COctetString(0-17)  # 有效期(可选)
registered_delivery : uint8               # 回执标志(如需要状态报告=0x01)
replace_if_present  : uint8               # 替换已存在消息(通常0x00)
data_coding         : uint8               # 编码格式(如GSM-7=0x00,UCS-2=0x08)
sm_default_msg_id   : uint8               # 默认消息ID(通常0x00)
sm_length           : uint8               # 短信内容长度
short_message       : OctetString(0-255)  # 短信内容(或UDH+内容)

4. 解析流程

  1. 读取Header:提取长度、命令类型及序列号。
  2. 验证长度:确保接收的数据完整。
  3. 解析Body:按命令类型对应的字段顺序逐个解析。
  4. 处理可选参数:遍历剩余字节解析TLV。
  5. 处理编码:根据<font style="color:rgb(51, 51, 51);">data_coding</font>转换内容(如GSM7、UCS-2)。
  6. 组装长短信:通过UDH中的<font style="color:rgb(51, 51, 51);">concat</font>参数合并分段。

5. 代码示例(Python)

import structdef parse_header(data):return struct.unpack('>4I', data[:16])def parse_submit_sm(body):fields = {}# 解析固定字段ptr = 0fields['service_type'] = parse_c_octet(body[ptr:ptr+6])    # COctetString(6)ptr += 6fields['source_addr_ton'] = body[ptr]ptr +=1# ... 按顺序解析其他字段# 解析short_messagesm_length = body[ptr]ptr +=1fields['short_message'] = body[ptr:ptr+sm_length]ptr += sm_length# 解析可选参数while ptr < len(body):tag, length = struct.unpack('>HH', body[ptr:ptr+4])ptr +=4value = body[ptr:ptr+length]ptr +=lengthfields[f'tag_{tag:04X}'] = valuereturn fieldsdef parse_c_octet(data):end = data.find(b'\x00')return data[:end].decode('latin1') if end != -1 else data.decode('latin1')

6. 注意事项
• 字节序处理:所有多字节整数需使用大端序解析。

• 版本兼容:不同版本的字段差异需适配(如5.0新增字段)。

• 错误处理:校验<font style="color:rgb(51, 51, 51);">command_status</font>,非零需处理错误码。

• 异步通信:通过<font style="color:rgb(51, 51, 51);">sequence_number</font>匹配请求与响应。

以下是针对SMPP协议的流程图、应用场景及Java代码示例:


一、流程图示例

  1. SMPP连接建立流程
    在这里插入图片描述

  2. 短信提交(submit_sm)流程

在这里插入图片描述

  1. 长短信分片处理流程
    在这里插入图片描述

二、应用场景

  1. 企业短信通知系统
    • 场景:企业向用户发送验证码或订单通知。

• SMPP角色:

• ESME(企业短信平台)通过submit_sm提交短信。

• SMSC(运营商网关)返回message_id,并在投递后发送状态报告(deliver_sm)。

  1. 用户上行短信(MO)处理
    • 场景:用户回复短信(如退订指令)。

• SMPP角色:

• SMSC主动推送deliver_sm到ESME,其中short_message字段包含用户回复内容。

• ESME解析后触发业务逻辑(如更新用户状态)。

  1. 国际短信路由
    • 场景:跨国企业向多国家用户发送短信。

• SMPP角色:

• 根据dest_addr_ton(目标地址类型)和data_coding(编码格式)自动适配运营商路由。

• 支持GSM-7、UCS-2等编码,确保内容兼容性。


三、Java代码示例

  1. 解析SMPP Header
public class SmppHeader {private int commandLength;private int commandId;private int commandStatus;private int sequenceNumber;public static SmppHeader parse(byte[] data) {ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);return new SmppHeader(buffer.getInt(),buffer.getInt(),buffer.getInt(),buffer.getInt());}
}
  1. 构造submit_sm PDU
public class SubmitSmBuilder {public static byte[] buildSubmitSm(String sourceAddr, String destAddr, String message) {ByteBuffer buffer = ByteBuffer.allocate(1024).order(ByteOrder.BIG_ENDIAN);// 固定字段buffer.put((byte) 0x00);                // service_type (空)buffer.put((byte) 0x01);                // source_addr_ton (国际号码)buffer.put((byte) 0x01);                // source_addr_npi (ISDN)putCOctetString(buffer, sourceAddr, 21); // source_addrbuffer.put((byte) 0x01);                // dest_addr_tonbuffer.put((byte) 0x01);                // dest_addr_npiputCOctetString(buffer, destAddr, 21);   // destination_addrbuffer.put((byte) 0x00);                // esm_classbuffer.put((byte) 0x00);                // protocol_idbuffer.put((byte) 0x00);                // priority_flag// 短信内容(GSM-7编码)byte[] gsm7Bytes = encodeGsm7(message);buffer.put((byte) gsm7Bytes.length);    // sm_lengthbuffer.put(gsm7Bytes);                  // short_message// 回退到实际长度并返回byte[] result = new byte[buffer.position()];System.arraycopy(buffer.array(), 0, result, 0, result.length);return result;}private static void putCOctetString(ByteBuffer buffer, String value, int maxLength) {byte[] bytes = value.getBytes(StandardCharsets.US_ASCII);int length = Math.min(bytes.length, maxLength - 1);buffer.put(bytes, 0, length);buffer.put((byte) 0x00); // 补\0结束符}
}
  1. 处理可选参数(TLV)
public class TlvParser {public static Map<Integer, byte[]> parseTlvs(ByteBuffer buffer) {Map<Integer, byte[]> tlvs = new HashMap<>();while (buffer.remaining() >= 4) {int tag = buffer.getShort() & 0xFFFF;int length = buffer.getShort() & 0xFFFF;byte[] value = new byte[length];buffer.get(value);tlvs.put(tag, value);}return tlvs;}
}

四、关键注意事项

  1. 编码处理
    • 使用StandardCharsets.US_ASCII处理GSM-7,StandardCharsets.UTF_16BE处理UCS-2。

• 长短信需添加UDH头,并通过sar_msg_ref_num等TLV参数关联分片。

  1. 异步通信
    • 通过sequenceNumber匹配请求和响应:
CompletableFuture<String> future = new CompletableFuture<>();
pendingRequests.put(sequenceNumber, future);
// 收到响应后通过sequenceNumber触发future.complete()
  1. 错误处理
    • 检查commandStatus
if (header.getCommandStatus() != 0) {throw new SmppException("Error code: " + header.getCommandStatus());
}

以上内容覆盖了SMPP的核心流程、场景及Java实现关键代码,可直接用于开发短信网关或集成服务。

相关文章:

  • UBUS 通信接口的使用——添加一个object对象(ubus call)
  • 日常开发小Tips:后端返回带颜色的字段给前端
  • Html1
  • SSR vs SSG:前端渲染模式终极对决(附 Next.js/Nuxt.js 实战案例)
  • 【MySQL】表的复合查询
  • Milvus(10):JSON 字段、数组字段
  • SpringBoot中获取系统及硬件信息
  • C++学习:六个月从基础到就业——模板编程:模板元编程基础
  • mermaid 序列图 解析
  • 如何用python脚本把一个表格有4万多条数据分为两个文件表,每个2万条数据?
  • 华为云IoT平台与MicroPython实战:从MQTT协议到物联网设备开发
  • 基于PHP的宠物用品商城
  • TCL科技2025一季度归母净利润10.1亿,半导体显示业务业绩创新高
  • 大模型备案实操手册:材料准备、流程解析与常见问题避坑指南
  • Spark GraphX 机器学习:图计算
  • 数据库所有知识
  • 如何设计一个会员码表!唯一索引的使用,字段区分度不高如何处理
  • 【AI面试准备】深度学习、大模型原理,算法项目经验
  • jthread是否可以完全取代thread?
  • Java高频面试之并发编程-11
  • 俄乌战火不熄,特朗普在梵蒂冈与泽连斯基会晤后口风突变
  • 杭州银行一季度净赚超60亿增逾17%,增速较去年同期有所回落
  • 苏州一季度GDP为6095.68亿元,同比增长6%
  • 吉林省公安厅出入境管理总队政委明志全已任省安保集团总经理
  • 安阳一村支书微信群骂村民被警方行拘,辩称对方先“污蔑造谣”
  • 央行副行长谈美债和美元波动:单一市场、单一资产的变动,对外储影响总体有限