天天看点

C#下的GSM短信实现

        很早就想写写博客什么的,一直都没实现,现在有点空闲,就随手写一点,主要介绍下我项目中c#下面实现GSM短信功能的代码,该代码主要适用在手机以及pda上。网上的资料零零散散,并且有很多问题,例如特殊字符解析不出来等。下面进入正题吧。

       以下主要讲解的是PDU格式发送短信。

       手机或者短信发送短信,是以串口通过AT指令的方式发送,PDU格式的短信格式为:短信中心号码类型+短信中心号+文件头字节+信息类型+被叫号码长度+被叫号码类型,下面以

       接收的手机号:13715342642

    短信中心号:8613800755000

    短信内容:你好,Hello!

为列,它解析成PDU格式的字符串为:“0891683108705500F011000D91683117352446F2000800124F60597DFF0C00480065006C006C006F0021

       ”08 - 指的是短信中心号的长度,也就是指(91)+( 683108705500F0)的长度

  

  91 - 指的是短信息中心号码类型。91是TON/NPI遵守International/E.164标准,指在号码前需加'+'号;此外还有其它数值,但91最常用。

  

  683108705500F0 - 短信息中心号码。由于位置上略有处理,实际号码应为:8613800731500(字母F是指长度减1)。这需要根据不同的地域作相应的修改。前面的(08)+(91)+( 683108705500F0)实际上就构成了整个短信的一部份,通称短消息中心地址(Address of the SMSC)。

  

     11 - 文件头字节

  

  00 - 信息类型(TP-Message-Reference)

  

  0D - 被叫号码长度

  

     91 - 被叫号码类型

  

  其实在实际处理中,我们通常把11000D91写死在程序中,因为在国内,这些数据都是不会改变的。

  

  683117352446F2 -被叫号码,经过了位移处理,实际号码为"8613715342642"。上面的(00)+(0D)+(91)+ ( 683117352446F2),构成了整个短信的第二部份目的地址(TP-Destination-Address)。

  

  00 - 协议标识TP-PID,这里一般为00

  

  08 - 数据编码方案TP-DCS(TP-Data-Coding-Scheme),采用前面说的USC2(16bit)数据编码

  

  00 - 有效期TP-VP(TP-Valid-Period)

  

  12-长度TP-UDL(TP-User-Data-Length),也就是4F60597DFF0C00480065006C006C的长度 36 / 2 = 18 的十六进 124F60597DFF0C00480065006C006C 006F0021- 这里就是短信内容了,实际内容为:"你好,Hello!"

        知道了理论,下面我们就来了解下C#如何编写

public class PduEncodeDecode
    {
        /// <summary>
        ///手机号码转换为pdu模式
         /// </summary>
        /// <param name="MobileNum"></param>
        /// <returns></returns>
        public string telc(string MobileNum)
        {
            int tl;
            string ltem, rtem, ttem;
            int ti;

            ttem = "";
            tl = MobileNum.Trim().Length;
            if (tl != 11 && tl != 13)
            {
                //MessageBox.Show("wrong number:" + MobileNum);
                return "";
            }

            if (tl == 11)  // 11位转换为13位
              {
                tl += 2;
                MobileNum = "86" + MobileNum;
            }

            for (ti = 0; ti < tl; ti += 2)
            {
                ltem = MobileNum.Substring(ti, 1);
                if (ti == tl - 1)
                {
                    rtem = "F";
                }
                else
                {
                    rtem = MobileNum.Substring(ti + 1, 1);
                }

                ttem += rtem + ltem;  //每两位颠倒
            }

            return ttem;
        }

        /// <summary>
        /// Unicode解码函数
         /// </summary>
        /// <param name="smsg"></param>
        /// <returns></returns>
        public string ascg(string smsg)
        {
            string res = "";
            string ls;
            string rs;

            byte[] resByte = System.Text.UnicodeEncoding.Unicode.GetBytes(smsg);
            for (int i = 0; i < resByte.Length; i += 2)
            {
                ls = resByte[i].ToString("X2");
                rs = resByte[i + 1].ToString("X2");
                res = res + rs + ls; //注意这里高低位颠倒
              }
            return res.Trim();
        }

        /// <summary>
        /// 发往发送方短信的PDU编码
        /// </summary>
        /// <param name="CenterNo">短信中心号码</param>
        /// <param name="PhoneNo">接收号码</param>
        /// <param name="Message">短信内容</param>
        /// <returns>返回PDU编码</returns>
        public string GetPduEncode(string CenterNo, string PhoneNo, string Message,out int length)
        {
            string prex = "0891";
            string midx = "11000D91";
            string sufx = "000800";

            string pdu, psmsc, pnum, pmsg;
            string leng;
           //  int length;


            length = (Message.Length) * 2;
            leng = length.ToString("X");

            if (length < 16)
            {
                leng = "0" + leng;
            }

            psmsc =telc(CenterNo.Trim());
            pnum = telc(PhoneNo.Trim());
            pmsg = ascg(Message.Trim());
            pdu = prex + psmsc + midx + pnum + sufx + leng + pmsg;//注意编码组合方式
            return pdu;
        }


        ///   <summary>   
        ///   判断接受的短信是PDU格式还是TEXT格式   
         ///   </summary>   

        public bool IsPDU(string SMS)
        {
            if ((SMS.Substring(40, 2) == "08") | (SMS.Substring(38, 2) == "08") || (SMS.Substring(34, 2) == "08"))
            //if (SMS.IndexOf("08111111")>-1)
                return true;
            else
                return false;
        }

        /// <summary>     
        /// 针对国际91(+)提取短信的发送人电话号码       
         ///   </summary>   
        /// <param name="SMS">要进行转换的整个短信内容</param>
        /// <returns>电话号码 </returns>

        public string GetTelphone(string SMS)
        {
            string tel = SMS.Substring(26, 12);
            string s = "";
            for (int i = 0; i < 11; i += 2)
            {
                s += tel[i + 1];
                s += tel[i];
            }
            s += tel[tel.Length -1];
            return s;
        }

        /// <summary>     
        /// 针对国内A1提取短信的发送人电话号码       
         ///   </summary>   
        /// <param name="SMS">要进行转换的整个短信内容</param>
        /// <returns>电话号码 </returns>

        public string GetTelphoneA1(string SMS)
        {
            string tel = SMS.Substring(24, 12);
            string s = "";
            for (int i = 0; i < 11; i += 2)
            {
                s += tel[i + 1];
                s += tel[i];
            }
            s += tel[tel.Length - 2];
            return s;
        }

     
        /// <summary>
        ///   函数功能:针对国际91(+)提取短信的发送时间          
         /// </summary>
        /// <param name="SMS">SMS:要进行转换的整个短信内容</param>
        /// <returns>发送时间 </returns>
        public string GetDataTime(string SMS)
        {
            string time = SMS.Substring(42, 12);
            string s = "";
            for (int i = 0; i < 11; i += 2)
            {
                s += time[i + 1];
                s += time[i];
            }
            string t = s.Substring(0, 2) + "年" + s.Substring(2, 2) + "月" + s.Substring(4, 2) + "日" + s.Substring(6, 2) + ":" + s.Substring(8, 2) + ":" + s.Substring(10, 2);
            return t;
        }

        /// <summary>
        ///   函数功能:针对国内A1提取短信的发送时间          
         /// </summary>
        /// <param name="SMS">SMS:要进行转换的整个短信内容</param>
        /// <returns>发送时间 </returns>
        public string GetDataTimeA1(string SMS)
        {
            string time = SMS.Substring(40, 12);
            string s = "";
            for (int i = 0; i < 11; i += 2)
            {
                s += time[i + 1];
                s += time[i];
            }
            string t = s.Substring(0, 2) + "年" + s.Substring(2, 2) + "月" + s.Substring(4, 2) + "日" + s.Substring(6, 2) + ":" + s.Substring(8, 2) + ":" + s.Substring(10, 2);
            return t;
        }


        /// <summary>
        /// 函数功能:针对国内A1USC2编码提取短信的内容(PDU)   
        /// </summary>
        /// <param name="SMS">SMS:要进行转换的整个短信内容</param>
        /// <returns>短信内容</returns>
        public string GetContentA1(string SMS)
        {
            string c = "";
            //string len = SMS.Substring(54, 2);
            string len = SMS.Substring(SMS.IndexOf("08111111")+14, 2);
            int length = System.Convert.ToInt16(len, 16);
            length *= 2;
            length = SMS.Length;
            string content = SMS.Substring(56+8, length - 56-8);
            for (int i = 0; i < length; i += 4)
            {
                if (i + 4 <= content.Length)
                {
                    string temp = content.Substring(i, 4);
                    int by = System.Convert.ToInt16(temp, 16);
                    char ascii = (char)by;
                    c += ascii.ToString();
                }
            }
            return c;   

        }

        /// <summary>
        /// 函数功能:针对国际(+91)USC2编码提取短信的内容(PDU)   
        /// </summary>
        /// <param name="SMS">SMS:要进行转换的整个短信内容</param>
        /// <returns>短信内容</returns>
        public string GetContent(string SMS)
        {
            string c = "";
            string len = SMS.Substring(56, 2);
            //string len = SMS.Substring(SMS.IndexOf("08111111") + 16, 2);
            int length = System.Convert.ToInt16(len, 16);
            length *= 2;
            string content = SMS.Substring(58, length);
            for (int i = 0; i < length; i += 4)
            {
                if (i + 4 <= content.Length)
                {
                    string temp = content.Substring(i, 4);
                    int by = System.Convert.ToInt16(temp, 16);
                    char ascii = (char)by;
                    c += ascii.ToString();
                }
            }
            return c;

        }
        /// <summary>
        /// 判断是否是国内A1编码
         /// </summary>
        /// <returns>返回布尔值</returns>
        public bool IsA1TypeNo(string SMS)
        { 
         string str=SMS.Substring(22,2);
         if(str=="A1")
         {
             return true ;
         }
         else
         {
             return false;
         }
        }
    }
           

      以上代码是把要发送的信息编译成PDU格式,下面介绍如何把PDU格式的字符串发送。

/// <summary>
        /// 发送短消息
         /// </summary>
        /// <param name="CenterNo">短信中心号码</param>
        /// <param name="PhoneNo">对方手机号</param>
        /// <param name="Message">发送信息内容</param>
        /// <returns></returns>
        public bool sendMsg(string CenterNo, string PhoneNo, string Message)
        {
            bool isSend = true;

            try
            {

                PduEncodeDecode pdendecode = new PduEncodeDecode();
                int length;
                string SMSPdu = pdendecode.GetPduEncode(CenterNo, PhoneNo, Message, out length);//编码成Pdu串
                   string recievedData = ExecCommand(port, "AT", 600, 100);
                Thread.Sleep(50);
                recievedData = ExecCommand(port, "AT+CMGF=0", 600, 100);
                Thread.Sleep(50);
                String command = "AT+CMGS=" + (15 + length).ToString();
                recievedData = ExecCommand(port, command, 600, 300);
                Thread.Sleep(50);
                string s = new string((char)26, 1);
                command = SMSPdu + s;
                ExecCommand(port, command, 3000, 300); //6 seconds
                Thread.Sleep(100);

                return isSend;
            }
            catch (Exception ex)
            {
                return isSend;
                //throw ex;
            }

        }
           

      只要先打开串口,然后调用sendMsg函数,就可以正常发送短信了。

      温馨提示:获取短信中心号需要和相应的厂家联系,不同厂家获取短信中心号的方式不同。

      希望这篇文章能够对大家有点作用。如有不当之处的还望能够和我探讨,另外也希望大家转载的时候注明出处。