使用PDU格式通过GSM网络发送短信的SmsToolset库
如何使用SmsToolset库通过PDU格式在GSM网络上发送短信
源代码也可在 github 仓库中找到: https://github.com/atmdevnet/SmsToolset.git
NuGet 包 (针对 .NET Standard 2.0) 可在以下位置找到: https://nuget.net.cn/packages/SmsToolset/
引言
SmsToolset 是一个用 C# 编写的 .NET 库,旨在帮助创建通过 GSM 网络以 PDU 格式发送和接收短信的应用程序。
该库的原始核心概念是 PDU profile,它是一个对象,用于确定传输的 PDU 包的结构。PDU profile 对象的基本部分是一个由 JSON 格式的文本文件驱动的 settings 对象。JSON 文件中定义的 profile 参数被加载到 settings 对象中,该对象初始化 PDU profile 对象。profile 的基本任务是创建 PDU 包。该库的另一个基本部分是 PDU profile manager,它有助于创建和管理许多不同的 profiles。
总的来说,该库包含三个主要部分,负责
- 创建和管理 PDU profiles
- 为通信设备(GSM 调制解调器)创建命令
- 向通信设备发送命令并接收响应
该库还包含一些工具的基本实现,有助于完成以下任务:
- 通过 PIN 码进行身份验证
- 处理服务中心号码
- 发送和读取短信
背景
库的结构包含三个命名空间。第一个命名空间 SmsTools.PduProfile
是库的核心。有两个主要的接口:IPduProfile
和 IPduProfileSettings
,以及 PduProfileManager
类。IPduProfileSettings
代表从 JSON 文件加载的 settings 对象,然后由实现 IPduProfile
接口的 profile 对象进行初始化。PduProfileManager
类的对象用于管理 profiles。库中实现了这些接口,分别是负责 profile 设置的 PduDefaultProfileSettings
,以及负责创建 profile 的 PduDefaultProfile
类。为什么这些类如此重要?因为这个 profile 对象负责创建将通过 GSM 网络发送的 PDU 包。PDU 包的结构和内容由 IPduProfileSettings
合约及其关联的 JSON 文件定义。应用给定的 settings 对象会决定包的格式及其元素:
- 服务中心号码,
- 目标号码,
- 使用的协议,
- 数据编码方案,
- 包的有效期,
- 包头信息,例如回复路径、用户头、状态报告、有效期格式、重复拒绝和消息类型。
另一个命名空间是 SmsTools.Commands
,它包含表示通信设备(DCE:GSM 调制解调器)理解的 AT 命令的接口。表示 AT 命令的对象的主要任务是形成传递给 DCE 的字符串,特别是 PDU 包,以及解释从 DCE 收到的响应。为了实现这个概念,使用了 IATCommand
和 ICommandParameter
接口,它们分别代表命令及其参数。命令的实现分为三个部分。SimpleATCommand
类负责创建不带任何参数的命令。ParamATCommand
允许创建带有单个参数的命令,而 StepwiseATCommand
用于在传递给命令的多个参数依赖于先前参数传递结果的情况下使用。发送短信的命令就是一个例子。
最后一个命名空间 SmsTools.Operations
包含表示 DCE 及其实现的接口。其主要且唯一的目的是向 DCE 发送命令并接收响应。IPortPlug
对象由命令用于通过 DCE 执行它们。
以上所有概念都实现在 Authentication
、ServiceCenter
和 PduSms
类中。Authentication 用于通过 PIN 码对用户进行身份验证。使用 ServiceCenter
类可以读取和设置服务中心号码。PduSms
允许以 PDU 包的形式发送短信。
Using the Code
让我们来看一个使用内置默认设置和默认 profile 发送 SMS 的最简单示例。
内置默认 PDU profile 的默认设置之一(在使用 PduSms
类时)如下:
- 服务中心号码类型:未设置,
- 服务中心号码:未设置,
- PDU 头:0x11,表示:
- 回复路径:未设置,
- 用户数据段仅包含消息内容,
- 不需要状态报告,
- 有效期字段有效且格式为相对格式,
- 不拒绝重复,
- 消息类型:SMS-SUBMIT。
- 目标号码类型:默认(不需要前缀),
- 目标号码:未设置,
- 协议标识符:sms,
- 数据编码方案:UCS2,
- 有效期:0xFF(最大值),
- 消息:未设置。
var ports = SerialPortPlug.AvailablePorts();
if (ports.Any())
{
var config = SerialPortConfig.CreateDefault();
config.Name = ports.First();
using (var modem = new SerialPortPlug(config))
{
if (!modem.IsOpen)
return;
var destination = 888123456;
var message = "hello";
var sms = new PduSms();
bool isSent = await sms.Send(modem, destination, message);
}
}
如何通过 PIN 码进行用户身份验证
var auth = new Authentication();
bool isAuthenticated = await auth.IsAuthenticated(modem);
if (!isAuthenticated)
{
isAuthenticated = await auth.Authenticate(modem, 1234);
}
如果需要,可以这样设置服务中心号码
var sca = new ServiceCenter();
var defined = await sca.IsDefined(modem);
if (defined)
{
bool international = await sca.HasInternationalFormat(modem);
var address = await sca.GetAddress(modem);
}
else
{
bool success = await sca.SetAddress(modem, 48601000310, true);
}
现在,让我们用一种稍微复杂一点的方式来做同样的事情(如第一个示例所示)。:)
首先,创建一个带有 profile 设置的 JSON 文件。设置如下:
- 服务中心号码类型:国际,
- 服务中心号码:+48 601 000 310,
- PDU 头:0x11,表示:
- 回复路径:未设置,
- 用户数据段仅包含消息内容,
- 不需要状态报告,
- 有效期字段有效且格式为相对格式,
- 不拒绝重复,
- 消息类型:SMS-SUBMIT。
- 目标号码类型:国际,
- 目标号码:未设置,
- 协议标识符:sms,
- 数据编码方案:UCS2,
- 有效期:0xFF(最大值),
- 消息:未设置。
JSON 文件内容
{
"sca": {
"type": 145,
"value": 48601000310
},
"pdu-header": {
"value": 17
},
"mr": {
"value": 0
},
"da": {
"type": 145,
"value": 0
},
"pid": {
"value": 0
},
"dcs": {
"value": 8
},
"vp": {
"value": 255
},
"ud": {
"value": ""
}
}
与 JSON 文件结构匹配的数据契约在 PduDefaultProfileSettings
类中定义,因此我们可以使用该类创建 settings 对象,然后基于加载的设置创建 profile。
var manager = new PduProfileManager();
using (var file = Assembly.GetExecutingAssembly().GetManifestResourceStream("profile.json"))
{
var settings = manager.CreateProfileSettings<PduDefaultProfileSettings>(file);
var profile = manager.CreateDefaultProfile(settings, "my_profile");
}
拥有 PDU profile 后,我们可以创建一个将在 GSM 网络上传输的 PDU 包。
int length = 0;
string packet = string.Empty;
long destination = 48888123456;
string message = "华为";
packet = profile.GetPacket(destination, message, out length);
在我们将消息作为 PDU 包发送之前,必须将消息格式设置为 PDU。
string commandParameterValue = Constants.MessageFormat.Pdu.ToValueString();
string successfulResponsePattern = Constants.BasicSuccessfulResponse;
var commandParameter = new CommandParameter(commandParameterValue, successfulResponsePattern);
string atCommand = ATCommand.MessageFormat.Command();
var command = new ParamATCommand(atCommand, commandParameter);
await command.ExecuteAsync(modem);
bool succeeded = command.Succeeded();
最后,我们可以发送我们的消息。
var lengthStep = new CommandParameter($"{length}{Constants.CR}",
Constants.ContinueResponse, false);
var packetStep = new CommandParameter($"{packet}{Constants.SUB}",
Constants.BasicSuccessfulResponse);
var sendCommand = new StepwiseATCommand(ATCommand.MessageSend.Command(),
new ICommandParameter[] { lengthStep, packetStep });
await sendCommand.ExecuteAsync(modem);
bool succeeded = sendCommand.Succeeded();
关注点
我希望您喜欢使用 PDU profile 以 PDU 消息格式发送 SMS 的想法。
历史
这是文章的第一个版本。