天天看點

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函數,就可以正常發送短信了。

      溫馨提示:擷取短信中心号需要和相應的廠家聯系,不同廠家擷取短信中心号的方式不同。

      希望這篇文章能夠對大家有點作用。如有不當之處的還望能夠和我探讨,另外也希望大家轉載的時候注明出處。