有两个原因要求我们的软件工程师及时知道我们的系统的任何部分工作异常: 1. 我们的系统24小时运行,每一个错误都会直接导致损失;2.我们的系统更新太快,没有完整的测试流程,难免会有些问题。

报警检查都不是从技术角度检查的, 而是从业务角度检查,比如订单下载是否正常,我们不是通过检查下载工具是否正常来确定的,而是通过检查下载结果确定的。另外,作为辅助,不是很紧急的异常,我们会用邮件报出来。


zabbix 短信报警配置 短信报警模块原理_数据

一 . 一 般 命 令
1.AT+CGMI      给出模块厂商的标识。
2.AT+CGMM    获得模块标识。这个命令用来得到支持的频带(GSM 900,DCS 1800    或PCS 1900)。当
模块有多频带时,回应可能是不同频带的结合。
3.AT+CGMR    获得模块的软件版本。
4.AT+CGSN    获得 GSM  模块的 IMEI(国际移动设备标识)序列号。
5.AT+CSCS    选择TE  特征设定。这个命令报告TE  用的是哪个状态设定上的 ME。
ME于是可以转换每一个输入的或显示的字母。这个是用来发送.读取或者撰写短信。
6.AT+WPCS    设定电话簿状态。这个特殊的命令报告通过 TE    电话簿所用的状态的 ME。
ME于是可以转换每一个输入的或者显示的字符串字母。这个用来读或者写电话簿的入口。
7.AT+CIMI    获得 IMSI。这命令用来读取或者识别 SIM  卡的IMSI (国际移动签署者标识)。  在读取
IMSI  之前应该先输入 PIN(如果需要 PIN 的话)码。
8.AT+CCID    获得 SIM  卡的标识。这个命令使模块读取 SIM卡上的 EF-CCID  文件。
9.AT+GCAP    获得能力表。(支持的功能)
10.A/           重复上次命令。只有 A/  命令不能重复。这命令重复前一个执行的命令。
11.AT+CPOF    关机。这个特殊的命令停止 GSM   软件堆栈和硬件层。命令 AT+CFUN=0 的功能
与+CPOF  相同。
12.AT+CFUN    设定电话机能。这个命令选择移动站点的机能水平。
13.AT+CPAS    返回移动设备的活动状态。
14.AT+CMEE    报告移动设备的错误。这个命令决定是否允许输出错误代码“+CME   ERROR:<xxx>”
或者“+CMSERROR:<xxx>”代替简单的“ERROR” 。
15.AT+CKPD    小键盘控制。仿真 ME小键盘执行命令。
16.AT+CCLK    时钟管理。这个命令用来设置或者获得 ME真实时钟的当前日期和时间。
17.AT+CALA    警报管理。这个命令用来设定在 ME  中的警报日期/时间。(闹铃)
18.AT+CRMP    铃声旋律播放。这个命令在模块的蜂鸣器上播放一段旋律。有两种旋律可用:语音来电
及数据或传真呼叫旋律和到来短信声音。
19.AT+CRSL    设定或获得到来的电话铃声的声音级别。
 
二 . 呼 叫 控 制 命 令
1.ATD           拨号命令。这个命令用来设置通话.数据或传真呼叫。
2.ATH           挂机命令。
3.ATA           接电话。
4.AT+CEER    扩展错误报告。这个命令给出当上一次通话设置失败后中断通话的原因。
5.AT+VTD      给用户提供应用 GSM  网络发送 DTMF(双音多频)双音频。这个命令用来定义双音
频的长度(默认值是 300 毫秒)。    
6.AT+VTS       给用户提供应用 GSM  网络发送 DTMF 双音频。这个命令允许传送双音频。
7.ATDL        重拨上次电话号码。
8.AT%Dn        数据终端就绪(DTR)时自动拨号。
9.ATS0          自动应答。
10.AT+CICB    来电信差。
11.AT+CSNS    单一编号方案。
12.AT+VGR,AT+VGT      增益控制。这个命令应用于调节喇叭的接收增益和麦克风的传输增益。
13.AT+CMUT               麦克风静音控制。
14.AT+SPEAKER             喇叭/麦克风选择。这个特殊命令用来选择喇叭和麦克风。
 Tech-Link T&E Limited                                                 
 
 
15.AT+ECHO        回音取消。
16.AT+SIDET          侧音修正。
17.AT+VIP          初始化声音参数。
18.AT+DUI           用附加的用户信息拨号。
19.AT+HUI           用附加的用户信息挂机。
20.AT+RUI           接收附加用户信息。
 
三 .网 络 服 务 命 令
1.AT+CSQ      信号质量。
2.AT+COPS    服务商选择。
3.AT+CREG    网络注册。获得手机的注册状态。
4.AT+WOPN    读取操作员名字。
5.AT+CPOL    优先操作员列表。
 
四 . 安 全 命 令
1.AT+CPIN            输入 PIN。
2.AT+CPIN2           输入 PIN2。
3.AT+CPINC         PIN 的剩余的尝试号码。
4.AT+CLCK          设备锁。
5.AT+CPWD           改变密码。
 
五 . 电 话 簿 命 令
1.AT+CPBS        选择电话簿记忆存储。
2.AT+CPBR        读取电话簿表目。
3.AT+CPBF        查找电话簿表目。
4.AT+CPBW       写电话簿表目。
5.AT+CPBP        电话簿电话查询。
6.AT+CPBN        电话簿移动动作。这个特殊命令使电话簿中的条目前移或后移(按字母顺序)
7.AT+CNUM       签署者号码。
8.AT+WAIP        防止在下一次重起时初始化所有的电话簿。
9.AT+WDCP       删除呼叫电话号码。
10.AT+CSVM    设置语音邮件号码。
 
六 . 短 消 息 命 令
1.AT+CSMS       选择消息服务。支持的服务有 GSM-MO.SMS-MT.SMS-CB。
2.AT+CNMA     新信息确认应答。
3.AT+CPMS       优先信息存储。这个命令定义用来读写信息的存储区域。
4.AT+CMGF    优先信息格式。执行格式有 TEXT    方式和 PDU    方式。
5.AT+CSAS      保存设置。保存+CSAS  和+CSMP  的参数。
6.AT+CRES       恢复设置。
7.AT+CSDH       显示文本方式的参数。
8.AT+CNMI       新信息指示。这个命令选择如何从网络上接收短信息。
9.AT+CMGR    读短信。信息从+CPMS    命令设定的存储器读取。
10.AT+CMGL    列出存储的信息。
11.AT+CMGS    发送信息。
12.AT+CMGW   写短信息并存储。
 Tech-Link T&E Limited                                                 
 
 
13.AT+CMSS       从存储器中发送信息。
14.AT+CSMP       设置文本模式的参数。
15.AT+CMGD    删除短信息。删除一个或多个短信息。
16.AT+CSCA       短信服务中心地址。
17.AT+CSCB      选择单元广播信息类型。
18.AT+WCBM    单元广播信息标识。
19.AT+WMSC    信息状态(是否读过.是否发送等等)修正。
20.AT+WMGO    信息覆盖写入。
21.AT+WUSS       不改变 SMS    状态。在执行+CMGR  或+CMGL 后仍保持 UNREAD。
 
七 . 追 加 服 务 命 令
1.AT+CCFC        呼叫继续。
2.AT+CLCK        呼叫禁止。
3.AT+CPWD        改变追加服务密码。
4.AT+CCWA        呼叫等待。
5.AT+CLIR         呼叫线确认限制。
6.AT+CLIP           呼叫线确认陈述。
7.AT+COLP        联络线确认陈述。
8.AT+CAOC        费用报告。
9.AT+CACM       累计呼叫计量。
10.AT+CAMM    累计呼叫计量最大值。
11.AT+CPUC       单价和货币表。
12.AT+CHLD       呼叫相关的追加服务。
13.AT+CLCC       列出当前的呼叫。
14.AT+CSSN       追加服务通知。
15.AT+CUSD       无组织的追加服务数据。
16.AT+CCUG       关闭的用户组。
 
八 . 数 据 命 令
1.AT+CBST         信差类型选择。
2.AT+FCLASS    选择模式。这个命令把模块设置成数据或传真操作的特殊模式。
3.AT+CR           服务报告控制。这个命令允许更为详细的服务报告。
4.AT+CRC         划分的结果代码。这个命令在呼叫到来时允许更为详细的铃声指示。
5.AT+ILRR       本地 DTE-DCE    速率报告。
6.AT+CRLP       无线电通信线路协议参数。
7.AT+DOPT        其他无线电通信线路参数。
8.AT%C           数据压缩选择。
9.AT+DS          是否允许 V42    二度数据压缩。
10.AT+DR           是否报告 V42    二度数据压缩。
11.AT/N           数据纠错选择。
 
九 . 传 真 命 令
1.AT+FTM      传送速率。
2.AT+FRM       接收速率
3.AT+FTH      用 HDLC协议设置传真传送速率。
4.AT+FRH      用 HDLC协议设置传真接收速率。
 Tech-Link T&E Limited                                                 
 
 
5.AT+FTS      停止特定时期的传送并等待。
6.AT+FRS       接收沉默。
 
十 . 第 二 类 传 真 命 令
1.AT+FDT        传送数据。
2.AT+FDR        接收数据。
3.AT+FET        传送页标点。
4.AT+FPTS       页转换状态参数。
5.AT+FK         终止会议。
6.AT+FBOR    页转换字节顺序。
7.AT+FBUF       缓冲大小报告。
8.AT+FCQ        控制拷贝质量检验。
9.AT+FCR        控制接收传真的能力。
10.AT+FDIS       当前会议参数。
11.AT+FDCC    设置 DCE    功能参数。
12.AT+FLID       定义本地 ID    串。
13.AT+FPHCTO       页转换超时参数。
 
十 一 .V25-V25 命 令
1.AT+IPR          确定 DTE    速率。
2.AT+ICF         确定 DTE-DCE  特征结构。
3.AT+IFC         控制 DTE-DCE  本地流量。
4.AT&C          设置 DCD(数据携带检测)信号。
5.AT&D          设置 DTR(数据终端就绪)信号。
6.AT&S          设置 DST(数据设置就绪)信号。
7.ATV            决定 DCE    响应格式。
8.ATZ            恢复为缺省设置。
9.AT&W          保存设置。
10.ATE           决定是否回显字符。
11.AT&F          回到出厂时的设定。
12.AT&V         显示模块设置情况。
13.AT+WMUX       数据/命令多路复用。
 
十 二 .特 殊 AT 命 令
1.AT+CCED         小区环境描述。
2.AT+WIND         一般指示。
3.AT+ADC          模拟数字转换度量。
4.AT+CMER         移动设备事件报告。这个命令决定是否允许在键按下时是否主动发送结果代码。
5.AT+WLPR          读取的语言编码。
6.AT+WLPW          写的语言编码。
7.AT+WIOR          读取 GPIO  值。
8.AT+WIOW          写 GPIO  值。
9.AT+WIOM          输入/输出口管理。
10.AT+WTONE       播放旋律。
11.AT+WDTMF       播放 DTMF    旋律。
12.AT+WDWL        进入软件下载模式。
 Tech-Link T&E Limited                                                 
 
 
13.AT+WVR            配置信差的声音速率。
14.AT+WDR          配置数据速率
15.AT+WHWV       显示硬件的版本。
16.AT+WDOP        显示产品的出厂日期。
17.AT+WSVG         声音增益选择。
18.AT+WSTR         返回指定状态的状态。
19.AT+WSCAN       扫描收到的信号强度。
20.AT+WRIM       设置或返回铃声指示模式
21.AT+W32K         是否允许 32kHz  省电方式。
22.AT+WCDM        改变缺省旋律。
23.AT+WSSW        显示内部软件版本。
24.AT+WCCS       编辑或显示订制性质设置表。
25.AT+WLCK       允许在特定的操作符上个性化 ME。
26.AT+CPHS        设置 CPHS  命令。
27.AT+WBCM        电池充电管理。
28.AT+WFM         特性管理。是否允许模块的某些特性,如频段模式及 SIM  卡电压等。
29.AT+WCFM        商业特性管理。是否允许 Wavecom  特殊特性。
30.AT+WMIR        允许从当前存储的参数值创建定制的存储镜像
31.AT+WCDP       改变旋律的缺省播放器。
 
十 三 .SIM  卡 工 具 箱 命 令
1.AT+STSF         配置工具箱实用程序。
2.AT+STIN        工具箱指示。
3.AT+STGI         获得从 SIM  卡发来的预期命令的信息。
4.AT+STCR        主动提供的结果:工具箱控制反应。
5.AT+STGR        给出响应。允许程序或用户从主菜单上选择项目,或响应某些命令。

AT 指令

先要把短信猫通过串口连到服务器上。

zabbix 短信报警配置 短信报警模块原理_数据_02

zabbix 短信报警配置 短信报警模块原理_数据

1 private SerialPort waveCommSerialPort;
 2 
 3         public void Initialize()
 4         {
 5             try
 6             {
 7                 string portName = ConfigurationManager.AppSettings["WAVECOMM"];//COM3
 8                 if (!string.IsNullOrEmpty(portName))
 9                 {
10                     if (waveCommSerialPort != null)
11                     {
12                         waveCommSerialPort.Close();
13                     }
14                     waveCommSerialPort = new System.IO.Ports.SerialPort();
15                     waveCommSerialPort.PortName = portName;
16                     waveCommSerialPort.BaudRate = 9600;
17                     waveCommSerialPort.Open();
18                     waveCommSerialPort.Close();
19                     bInitializeSuccessful = true;
20                 }
21             }
22             catch
23             {
24                 if (waveCommSerialPort != null) waveCommSerialPort.Close();
25             }
26         }

Serial Port Init

准备好从Serial Port 读写信息

zabbix 短信报警配置 短信报警模块原理_数据_02

zabbix 短信报警配置 短信报警模块原理_数据

1 private string RESULT_STRING = "";
 2         private string SUCCESS_KEYWORD = "OK";
 3         private ManualResetEvent COMMAND_SIGNAL = new ManualResetEvent(false);
 4 
 5         public bool SendCommand(string strCommand, string strSuccessKeyword)
 6         {
 7             string strRslt = "";
 8             return SendCommand(strCommand, strSuccessKeyword, ref strRslt);
 9         }
10 
11         public bool SendCommand(string strCommand, string strSuccessKeyword, ref string result)
12         {
13             if (!bInitializeSuccessful) return false;
14 
15             try
16             {
17                 if (!waveCommSerialPort.IsOpen) waveCommSerialPort.Open();
18 
19                 RESULT_STRING = "";
20                 SUCCESS_KEYWORD = strSuccessKeyword;
21 
22                 COMMAND_SIGNAL.Reset();
23                 waveCommSerialPort.DiscardNull = true;
24                 waveCommSerialPort.DiscardInBuffer();
25                 waveCommSerialPort.DiscardOutBuffer();
26                 waveCommSerialPort.Write(strCommand);
27                 waveCommSerialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(SerialPort_DataReceived);
28                 waveCommSerialPort.ErrorReceived += new SerialErrorReceivedEventHandler(SerialPort_ErrorReceived);
29                 bool isSuccess = COMMAND_SIGNAL.WaitOne(10000);
30                 result = RESULT_STRING;
31                 return isSuccess;
32             }
33             catch
34             {
35             }
36             finally
37             {
38                 RESULT_STRING = "";
39                 SUCCESS_KEYWORD = "OK";
40                 waveCommSerialPort.DataReceived -= new System.IO.Ports.SerialDataReceivedEventHandler(SerialPort_DataReceived);
41                 waveCommSerialPort.ErrorReceived -= new SerialErrorReceivedEventHandler(SerialPort_ErrorReceived);
42             }
43             return false;
44         }
45 
46         void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
47         {
48             throw new NotImplementedException();
49         }
50 
51         void SerialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
52         {
53             int bytestoread = this.waveCommSerialPort.BytesToRead;
54             if (bytestoread > 0)
55             {
56                 RESULT_STRING += this.waveCommSerialPort.ReadExisting();
57                 if (!string.IsNullOrEmpty(SUCCESS_KEYWORD) && RESULT_STRING.Contains(SUCCESS_KEYWORD))
58                 {
59                     COMMAND_SIGNAL.Set();
60                 }
61             }
62             else
63             {
64                 COMMAND_SIGNAL.Set();
65             }
66         }
67 
68         private bool WaitSucceedKeyWord(string strSucceedKeyWord, ref string strRtn)
69         {
70             strRtn = "";
71             int i = 0;
72 
73             while (!strRtn.Contains(strSucceedKeyWord))
74             {
75                 if (strRtn.Contains("ERROR")) return false;
76                 System.Threading.Thread.Sleep(10);
77                 var newString = this.waveCommSerialPort.ReadExisting();
78                 if (string.IsNullOrEmpty(newString))
79                 {
80                     if (i++ > 500) return false;
81                 }
82                 else
83                     i = 0;
84                 strRtn += newString;
85             }
86             System.Threading.Thread.Sleep(100);
87             strRtn += this.waveCommSerialPort.ReadExisting();
88             return true;
89         }

Send SMS Command

初始化短信模块

zabbix 短信报警配置 短信报警模块原理_数据_02

zabbix 短信报警配置 短信报警模块原理_数据

1             this.SendCommand("AT+CNMI=2,1,0,1\r\n", "OK");// config the way of receiving SMS
2             this.SendCommand("AT+CMEE=1\r\n", "OK");// config error report

SMS Init

发送短信,文本方式

zabbix 短信报警配置 短信报警模块原理_数据_02

zabbix 短信报警配置 短信报警模块原理_数据

1         public bool SendTEXT(string mobile, string msgContent)
 2         {
 3             try
 4             {
 5                 if (!waveCommSerialPort.IsOpen) waveCommSerialPort.Open();
 6                 if (!SendCommand("AT\r\n", "OK"))
 7                 {
 8                     return false;
 9                 }
10 
11                 if (!SendCommand("AT+CMGF=1\r\n", "OK"))
12                 {
13                     return false;
14                 }
15 
16                 var outstr = string.Format("AT+CMGS=\"{0}\"\r", mobile, "OK");
17                 if (!SendCommand(outstr, ">"))
18                 {
19                     return false;
20                 }
21 
22                 outstr = msgContent + "\x001a";
23                 if (!SendCommand(outstr.Trim(), "OK"))
24                 {
25                     return false;
26                 }
27                 return true;
28             }
29             catch (Exception ex)
30             {
31                 Trace.WriteLine(ex.Message);
32                 return false;
33             }
34             finally
35             {
36                 waveCommSerialPort.Close();
37             }
38         }

Send SMS(Text)

发送短信,PDU方式

zabbix 短信报警配置 短信报警模块原理_数据_02

zabbix 短信报警配置 短信报警模块原理_数据

1 public bool SendPDU(string mobile, string msgContent)
 2         {
 3             try
 4             {
 5                 var pdu = GetPDU(mobile, msgContent);
 6                 var len = (pdu.Length / 2) - 1;
 7                 if (!waveCommSerialPort.IsOpen) waveCommSerialPort.Open();
 8                 if (!SendCommand("AT\r\n", "OK"))
 9                 {
10                     return false;
11                 }
12 
13                 if (!SendCommand("AT+CMGF=0\r\n", "OK"))
14                 {
15                     return false;
16                 }
17 
18                 var outstr = string.Format("AT+CMGS={0}\r", len, "OK");
19                 if (!SendCommand(outstr, ">"))
20                 {
21                     return false;
22                 }
23 
24                 outstr = pdu.Trim() + "\x001a";
25                 if (!SendCommand(outstr.Trim(), "OK"))
26                 {
27                     return false;
28                 }
29                 return true;
30             }
31             catch (Exception ex)
32             {
33                 Trace.WriteLine(ex.Message);
34                 return false;
35             }
36             finally
37             {
38                 waveCommSerialPort.Close();
39             }
40         }

Send SMS(PDU)

PDU方式需要将消息格式转一下

zabbix 短信报警配置 短信报警模块原理_数据_02

zabbix 短信报警配置 短信报警模块原理_数据

1         /// <summary>
 2         /// 将手机号码的奇偶数为替换,缺少末尾偶数位的话用F来代替
 3         /// </summary>
 4         /// <param name="strMobile">手机号码(前面加国际编号:中国86美国01)</param>
 5         /// <returns></returns>
 6         private static string ConverMobile(string strMobile)
 7         {
 8             if (strMobile.Length == 11)
 9             {
10                 strMobile = "86" + strMobile;
11             }
12             string strRtn = "";
13             int length = strMobile.Length;
14             for (int i = 0; i < length; i += 2)
15             {
16                 string str2;
17                 string str1 = strMobile.Substring(i, 1);
18                 if ((i + 1) == length)
19                 {
20                     str2 = "F";
21                 }
22                 else
23                 {
24                     str2 = strMobile.Substring(i + 1, 1);
25                 }
26                 strRtn = strRtn + str2 + str1;
27             }
28             return strRtn;
29         }
30 
31         /// <summary>
32         /// 短信内容转PDU
33         /// </summary>
34         /// <param name="strMsg">短信内容</param>
35         /// <returns></returns>
36         private static string ConverUniCode(string strMsg)
37         {
38             var encodingUTF = System.Text.Encoding.BigEndianUnicode;
39             string s = null;
40             byte[] encodedBytes = encodingUTF.GetBytes(strMsg);
41             for (int i = 0; i < encodedBytes.Length; i++)
42             {
43                 s += BitConverter.ToString(encodedBytes, i, 1);
44             }
45             return s;
46         }
47 
48         /// <summary>
49         /// 将短信内容和手机号码转换成pdu格式
50         /// </summary>
51         /// <param name="mobile">手机号码(前面加国际编号:中国86美国01)</param>
52         /// <param name="msgContent">短信内容</param>
53         /// <returns></returns>
54         private static string GetPDU(string mobile, string msgContent)
55         {
56             string str = "0011000DA1";
57             string strMobilePDU = ConverMobile(mobile);
58             string strMsgContentPDU = ConverUniCode(msgContent);
59             int num = strMsgContentPDU.Length / 2;
60             string str5 = num.ToString("X");
61             if (num < 0x10)
62             {
63                 str5 = "0" + str5;
64             }
65             return (str + strMobilePDU + "0008A7" + str5 + strMsgContentPDU);
66         }

Transfer to PDU code

读短信

zabbix 短信报警配置 短信报警模块原理_数据_02

zabbix 短信报警配置 短信报警模块原理_数据

1 public SMSLog ReadTextMsg(int index)
 2         {
 3             SMSLog log = null;
 4             if (index < 0 || index > 50) return log;
 5 
 6             string result = string.Empty;
 7             if (SendCommand("AT+CMGF=0\r\n", "OK", ref result))
 8             {
 9                 if (SendCommand("AT+CMGR=" + index.ToString() + "\r\n", "OK", ref result))
10                 {
11                     log = new SMSLog()
12                     {
13                         Id = Guid.NewGuid(),
14                         SMSType = SMSType.Receive
15                     };
16 
17                     string[] strMsg = result.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
18                     if (strMsg.Length >= 3)
19                     {
20                         if (strMsg[1].Contains("+CMGR:"))
21                         {
22                             string code = "";
23                             string sca = "";
24                             PDUDecoding.DecodingMsg(strMsg[2], ref log.PhoneFrom, ref log.SMSContent, ref log.ReceiveTime, ref code, ref sca);
25                         }
26                     }
27                 }
28             }
29 
30             if (string.IsNullOrEmpty(log.SMSContent) && log.PhoneFrom.Length > 50)
31             {
32                 log.SMSContent = log.PhoneFrom;
33                 log.PhoneFrom = "";
34             }
35             return log;
36         }

Read SMS

 PDU 解码

zabbix 短信报警配置 短信报警模块原理_数据_02

zabbix 短信报警配置 短信报警模块原理_数据

public class PDUDecoding
    {
        static public bool DecodingMsg(string s, ref string phone, ref string text, ref DateTime sendTime, ref string code, ref string SCA)
        {
            try
            {
                //短信中心号码解码 
                int iLength = int.Parse(s.Substring(0, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
                if (iLength > 0)
                {
                    if (s.Substring(2, 2) == "91")
                    {
                        SCA += "+";
                        iLength--;
                    }
                    for (int j = 0; j < iLength * 2; j += 2)
                    {
                        SCA += s.Substring(5 + j, 1);
                        SCA += s.Substring(4 + j, 1);
                    }
                    if (SCA.EndsWith("F")) SCA = SCA.Remove(SCA.Length - 1, 1);
                }
                s = s.Remove(0, iLength * 2 + 6);

                //发送方手机号码解码 
                iLength = int.Parse(s.Substring(0, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
                if (s.Substring(2, 2) == "91")
                {
                    phone = "+";
                }
                if (iLength % 2 == 1) iLength++;
                for (int j = 0; j < iLength; j += 2)
                {
                    phone += s.Substring(5 + j, 1);
                    phone += s.Substring(4 + j, 1);
                }
                if (phone.EndsWith("F")) phone = phone.Remove(phone.Length - 1, 1);
                s = s.Remove(0, iLength + 6);

                //编码方式 
                if (s.Substring(0, 2) == "08")
                    code = "uc";
                else if (s.Substring(0, 2) == "00")
                    code = "7";
                else
                    code = "8";
                s = s.Remove(0, 2);

                //短信发送时间解码 
                sendTime = new DateTime(int.Parse("20" + s.Substring(1, 1) + s.Substring(0, 1)),
                    int.Parse(s.Substring(3, 1) + s.Substring(2, 1)),
                    int.Parse(s.Substring(5, 1) + s.Substring(4, 1)),
                    int.Parse(s.Substring(7, 1) + s.Substring(6, 1)),
                    int.Parse(s.Substring(9, 1) + s.Substring(8, 1)),
                    int.Parse(s.Substring(11, 1) + s.Substring(10, 1)));
                s = s.Remove(0, 16);
                if (code == "uc")
                {
                    text = DecodingUCS2(s);
                }
                else if (code == "7")
                {
                    text = DecodingBit7(s);
                }
                else
                {
                    text = DecodingBit8(s);
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        //中文短信息UCS2解码 
        static public string DecodingUCS2(string s)
        {
            byte[] buf = new byte[s.Length];
            for (int i = 0; i < s.Length; i += 4)
            {
                buf[i / 2] = byte.Parse(s.Substring(2 + i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
                buf[i / 2 + 1] = byte.Parse(s.Substring(i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
            }
            return Encoding.Unicode.GetString(buf);
        }

        // 使用8-bit进行解码 
        static public string DecodingBit8(string s)
        {
            byte[] buf = new byte[s.Length / 2];
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < s.Length; i += 2)
            {
                buf[i / 2] = byte.Parse(s.Substring(i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
            }
            return Encoding.ASCII.GetString(buf);
        }

        // 使用7-bit进行解码 
        static public string DecodingBit7(string s)
        {
            int iByte = 0;
            int iLeft = 0;
            var sb = new System.Text.StringBuilder();
            for (int i = 0; i < s.Length; i += 2)
            {
                byte bSrc = byte.Parse(s.Substring(i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
                sb.Append((((bSrc << iByte) | iLeft) & 0x7f).ToString("X2"));
                iLeft = bSrc >> (7 - iByte);
                iByte++;
                if (iByte == 7)
                {
                    sb.Append(iLeft.ToString("X2"));
                    iByte = 0;
                    iLeft = 0;
                }
            }
            string sReturn = sb.ToString();
            byte[] buf = new byte[sReturn.Length / 2];
            for (int i = 0; i < sReturn.Length; i += 2)
            {
                buf[i / 2] = byte.Parse(sReturn.Substring(i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
            }
            return System.Text.Encoding.ASCII.GetString(buf);
        }

    }

PDU DeCode