2012-08-24 | #1 (permalink) |
论坛管理员
注册日期: 2009-06-30
帖子: 861
|
中英文手机短信 PDU 串 编码(UCS2) 解码(UCS2,7-Bit) 程序 ( in C# )
中英文手机短信 PDU 串 编码(UCS2) 解码(UCS2,7-Bit) 程序 ( in C# )
//===================================================================================== /* 关于手机短信(SMS)的编码模式及其规则,读者可以查阅相关规范, 如 GSM03.08, GSM03.40, GSM07.05等. 科脑工作室的 bhw98 先生在文献<<通过串口收发短消息(上/下篇)>>中通过实例给出了 详细说明, <<Visual C++/Turbo C 串口通信编程实践>>(电子工业出版社)一书中对其原文进行了全 面重载, 有兴趣的读者可参考以上文献. 本文给出通过串口收发手机中英文短信的全部 C# 实现过程.中英文短信内容编码均采用UCS2模 式, 考虑到有些手机在纯英文模式下编码为 7-Bit模式, 为了能对这些内容解读, 文中也提供了 7-Bit 模式短信内容解码程序; 文中编码解码过程未使用任何第三方控件. 文中提供了两个主要函数 SendSms()和 ReceiveSms(), 直接调用就可实现短信发送和接收, 例 如: SendSms("13800755500","13423714101","神州六号将于近期发射!"); 即完成发送; ReceiveSms(); 即完成接收, 内容存于字符数组 ReceTele[],ReceTime[],ReceText[]中; 开发平台: Windows 2003 Server; Visual Studio.NET 2003 ;添加 VS 98 MSComm控件; 硬件: 西门子TC35i终端或其它无线MODEM; 西门子TC35i终端AT指令集可从以下网站下载: http://www.wlt.net.cn/index0/tc35i/tc35i.htm, 文件名为:tc35i_atc_v0103_1073581.pdf 主要程序代码如下: [作者约定: 若某一行以注释开头, 则该注释用于说明该行后面内容;否则, 若以代码开头,则其后 注释用于说明本行内容] */ //===================================================================================== using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Text; //Encoding 等函数名字空间 using System.Threading; // Thread.Sleep 函数名字空间 namespace TC35iSMS { public class Form1 : System.Windows.Forms.Form { //下列字符串数组用于存放已收短信相关内容 private string[] ReceTele=new string[40]; //电话号码 private string[] ReceTime=new string[40]; //日期时间 private string[] ReceText=new string[40]; //短信正文 private System.Windows.Forms.TextBox txtSmscNumber; private System.Windows.Forms.ListBox RecMessListBox; private System.Windows.Forms.TextBox txtDestNumber; private System.Windows.Forms.TextBox txtSmsText; private System.Windows.Forms.Button btnReceive; private System.Windows.Forms.Label lbMessage; private System.Windows.Forms.Button btnSend; private System.Windows.Forms.Label label3; //MsComm串口控件,可从 VS 98 选择安装;即将发行的 VS.NET 2005 中 //自带有串口控件,用户可直接使用 private AxMSCommLib.AxMSComm COM1; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.ComponentModel.Container components = null; public Form1() { InitializeComponent(); } protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows 窗体设计器生成的代码 /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() { System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1)); this.txtSmscNumber = new System.Windows.Forms.TextBox(); this.RecMessListBox = new System.Windows.Forms.ListBox(); this.txtDestNumber = new System.Windows.Forms.TextBox(); this.txtSmsText = new System.Windows.Forms.TextBox(); this.btnReceive = new System.Windows.Forms.Button(); this.lbMessage = new System.Windows.Forms.Label(); this.btnSend = new System.Windows.Forms.Button(); this.label3 = new System.Windows.Forms.Label(); this.COM1 = new AxMSCommLib.AxMSComm(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.COM1)).BeginInit(); this.SuspendLayout(); // // txtSmscNumber // this.txtSmscNumber.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(134))); this.txtSmscNumber.ForeColor = System.Drawing.Color.Blue; this.txtSmscNumber.Location = new System.Drawing.Point(24, 32); this.txtSmscNumber.MaxLength = 11; this.txtSmscNumber.Name = "txtSmscNumber"; this.txtSmscNumber.Size = new System.Drawing.Size(80, 21); this.txtSmscNumber.TabIndex = 42; this.txtSmscNumber.Text = "13800755500"; // // RecMessListBox // this.RecMessListBox.ItemHeight = 12; this.RecMessListBox.Location = new System.Drawing.Point(8, 96); this.RecMessListBox.Name = "RecMessListBox"; this.RecMessListBox.ScrollAlwaysVisible = true; this.RecMessListBox.Size = new System.Drawing.Size(536, 280); this.RecMessListBox.TabIndex = 41; // // txtDestNumber // this.txtDestNumber.ForeColor = System.Drawing.Color.Blue; this.txtDestNumber.Location = new System.Drawing.Point(552, 120); this.txtDestNumber.MaxLength = 13; this.txtDestNumber.Name = "txtDestNumber"; this.txtDestNumber.Size = new System.Drawing.Size(88, 21); this.txtDestNumber.TabIndex = 37; this.txtDestNumber.Text = "13423714101"; // // txtSmsText // this.txtSmsText.ForeColor = System.Drawing.Color.Blue; this.txtSmsText.Location = new System.Drawing.Point(208, 8); this.txtSmsText.MaxLength = 70; this.txtSmsText.Multiline = true; this.txtSmsText.Name = "txtSmsText"; this.txtSmsText.Size = new System.Drawing.Size(432, 80); this.txtSmsText.TabIndex = 35; this.txtSmsText.Text = ""; // // btnReceive // this.btnReceive.Font = new System.Drawing.Font("宋体", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(134))); this.btnReceive.ForeColor = System.Drawing.Color.Blue; this.btnReceive.Location = new System.Drawing.Point(560, 248); this.btnReceive.Name = "btnReceive"; this.btnReceive.Size = new System.Drawing.Size(80, 24); this.btnReceive.TabIndex = 40; this.btnReceive.Text = "接 收"; this.btnReceive.Click += new System.EventHandler(this.btnReceive_Click); // // lbMessage // this.lbMessage.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(134))); this.lbMessage.ForeColor = System.Drawing.Color.Red; this.lbMessage.Location = new System.Drawing.Point(16, 64); this.lbMessage.Name = "lbMessage"; this.lbMessage.Size = new System.Drawing.Size(184, 23); this.lbMessage.TabIndex = 38; // // btnSend // this.btnSend.Font = new System.Drawing.Font("宋体", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(134))); this.btnSend.Location = new System.Drawing.Point(560, 184); this.btnSend.Name = "btnSend"; this.btnSend.Size = new System.Drawing.Size(80, 23); this.btnSend.TabIndex = 1; this.btnSend.Text = "发 送"; this.btnSend.Click += new System.EventHandler(this.btnSend_Click); // // label3 // this.label3.ForeColor = System.Drawing.Color.Blue; this.label3.Location = new System.Drawing.Point(144, 8); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(56, 16); this.label3.TabIndex = 34; this.label3.Text = "发送内容"; // // COM1 // this.COM1.Enabled = true; this.COM1.Location = new System.Drawing.Point(96, 32); this.COM1.Name = "COM1"; this.COM1.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("COM1.OcxState"))); this.COM1.Size = new System.Drawing.Size(38, 38); this.COM1.TabIndex = 43; // // label1 // this.label1.ForeColor = System.Drawing.Color.Black; this.label1.Location = new System.Drawing.Point(8, 8); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(112, 16); this.label1.TabIndex = 44; this.label1.Text = "中心号码(不需+86)"; // // label2 // this.label2.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(134))); this.label2.ForeColor = System.Drawing.Color.Blue; this.label2.Location = new System.Drawing.Point(568, 96); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(56, 16); this.label2.TabIndex = 45; this.label2.Text = "对方号码"; // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(6, 14); this.ClientSize = new System.Drawing.Size(648, 381); this.Controls.Add(this.label2); this.Controls.Add(this.label1); this.Controls.Add(this.COM1); this.Controls.Add(this.txtSmscNumber); this.Controls.Add(this.RecMessListBox); this.Controls.Add(this.txtDestNumber); this.Controls.Add(this.txtSmsText); this.Controls.Add(this.btnReceive); this.Controls.Add(this.lbMessage); this.Controls.Add(this.btnSend); this.Controls.Add(this.label3); this.Name = "Form1"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "中英文手机短信 PDU 串 编码(UCS2) 解码(UCS2,7-Bit) 程序 ( in C# by 李仓海 )"; this.Load += new System.EventHandler(this.Form1_Load); ((System.ComponentModel.ISupportInitialize)(this.COM1)).EndInit(); this.ResumeLayout(false); } #endregion [STAThread] static void Main() { Application.Run(new Form1()); } //===================================================================================== private void Form1_Load(object sender, System.EventArgs e) { COM1.CommPort=1; //硬件连于 COM1 //串口设定: 波特率19200,无校验,8位数据位,1位停止位 COM1.Settings="19200,n,8,1"; COM1.InBufferSize=1024*8; //8K 读缓冲 COM1.PortOpen=true;//打开串口 for(int i=0;i<40;i++) //字符串数组清空 { ReceTele[i]=""; //电话号码 ReceTime[i]=""; //日期时间 ReceText[i]=""; //短信正文 } } //===================================================================================== private void btnSend_Click(object sender, System.EventArgs e) //点击发送按扭 { this.btnSend.Enabled=false; //发送过程其它功能按扭禁用 this.btnReceive.Enabled=false; this.SendSms(this.txtSmscNumber.Text,this.txtDestNumber.Text, this.txtSmsText.Text); this.btnSend.Enabled=true; //开启功能按扭 this.btnReceive.Enabled=true; } //===================================================================================== private void btnReceive_Click(object sender, System.EventArgs e)//点击接收按扭 { this.btnSend.Enabled=false;//接收过程其它功能按扭禁用 this.btnReceive.Enabled=false; this.ReceiveSms(); //调用接收函数, 接收内容存于相应数组 this.RecMessListBox.Items.Clear();//短信显示栏清空 int Sms_Number=0; //短信数目变量 for (int i=0;i<40;i++) //显示已收短信内容 { if ( ReceText[i].ToString().Length>0) { Sms_Number+=1; this.RecMessListBox.Items.Add("来自:"+ReceTele[i]+ " 时间:"+ReceTime[i]); this.RecMessListBox.Items.Add("内容:"+ReceText[i]); this.RecMessListBox.Items.Add(" "); //短信间隔用空行 ReceTele[i]=""; //显示后字符串数组清空 ReceTime[i]=""; ReceText[i]=""; } } if(Sms_Number==0) this.lbMessage.Text="暂无短信!"; else this.lbMessage.Text="收到 "+Sms_Number.ToString()+" 条新短信!"; this.btnSend.Enabled=true;//开启功能按扭 this.btnReceive.Enabled=true; } //===================================================================================== private void SendSms(string Smsc_Number,string Dest_Number,string Sms_Text)//发送函数 { string pdu=""; //开始合成 PDU 串 pdu+="089168"; //SMSC中心号码补F凑成偶数 char[] tmpSmscNumber=(Smsc_Number+"F").ToCharArray(); for(int i=0;i<tmpSmscNumber.Length;i+=2) //字符两两对调 { pdu+=tmpSmscNumber[i+1].ToString(); pdu+=tmpSmscNumber[i].ToString(); } pdu+="11000D9168"; char[] tmpDestNumber=(Dest_Number+"F").ToCharArray();//对方号码 for(int i=0;i<tmpDestNumber.Length;i+=2) //字符两两对调 { pdu+=tmpDestNumber[i+1].ToString(); pdu+=tmpDestNumber[i].ToString(); } pdu+="000800"; //08 表示采用 UCS2 编码 //短信正文转为Unicode byte[] tmpSmsText=Encoding.Unicode.GetBytes(Sms_Text); pdu+=tmpSmsText.Length.ToString("X2"); //正文内容长度 for(int i=0;i<tmpSmsText.Length;i+=2) //高低字节对调 { pdu+=tmpSmsText[i+1].ToString("X2");//("X2")转为16进制 pdu+=tmpSmsText[i].ToString("X2"); } //PDU串 完成 int tmpLength=(pdu.Length-18)/2;//除去SMSC段长度 //向串口发Send an SMS 命令 this.COM1.Output="AT+CMGC="+tmpLength+(char)13; Thread.Sleep(500);//延时 0.5 秒 this.COM1.Output=pdu;//输出 PDU 串 this.COM1.Output=((char)26).ToString();//结束 Thread.Sleep(1500);//延时 1.5 秒 } //===================================================================================== private void ReceiveSms() //接收函数 { //向串口发 List SMS Messages from preferred store 指令 this.COM1.Output="AT+CMGL=0"+(char)13; Thread.Sleep(1000); //延时 1 秒 if(this.COM1.InBufferCount<=0) return; string ans="";//清空应答串 //该循环用于当卡中有多条(>=5)未读短信时可一次全部读出 while(this.COM1.InBufferCount>0) { ans+=this.COM1.Input.ToString(); //将串口 Buff 内容存入应答串 ans Thread.Sleep(3000); //延迟3秒 } //下列循环将已读短信逐一从SIM卡内存中删除,否则若SIM卡内存不足将再无发收发! for (int i=1;i<=40;i++) { string m_id="+CMGL: "+i.ToString(); if(ans.IndexOf(m_id,0)>=0) //取得 SIM 卡内存中短信 ID 号 { Thread.Sleep(500);//延时 0.5 秒 this.COM1.Output="AT+CMGD="+i+(char)13; //删除SIM卡内存中短信 } } //开始查找是否有合格 PDU 串 if(ans.IndexOf("+CMGL:",0)<0 ||ans.IndexOf("OK",0)<0 || ans.IndexOf("ERROR",0)>=0) { ans=""; return;} ans=ans.Substring(ans.IndexOf("+CMGL:",0)); int MessNo=0; //存储短信相关内容的数组下标 string tmpPdu=""; while(ans.IndexOf("0891")!=-1) { ans=ans.Substring(ans.IndexOf("0891",0)); int NextOne=ans.IndexOf("+CMGL:"); if(NextOne!=-1) //同时收到多条 SMSes { tmpPdu=ans.Substring(0,NextOne-2);//读取第一条PDU串 ans=ans.Substring(NextOne);//定位到下一条 } else { int LastOk=ans.IndexOf("OK",0); //仅收到一条 SMS tmpPdu=ans.Substring(0,LastOk-4);//读取PDU串 ans="";//应答串清空 } string EncodeType=tmpPdu.Substring(40,2);//编码方式 if(EncodeType=="08" || EncodeType=="00") { ReceTele[MessNo]=this.GetTeleFromPdu(tmpPdu);//电话号码 ReceTime[MessNo]=this.GetTimeFromPdu(tmpPdu);//日期时间 //以下为短信正文解析 if(EncodeType=="08") //UCS2 解码 ReceText[MessNo]=this.GetTextFromPdu_UCS2(tmpPdu); else //7-Bit 解码 ReceText[MessNo]=this.GetTextFromPdu_7Bit(tmpPdu); MessNo+=1; } } // end of while } //===================================================================================== private string GetTeleFromPdu(string pdu) //解析 TeleNumber 函数 { //截取PDU串中短信发送方电话号码源码 string TeleInPdu=pdu.Substring(26,12); string Tele=""; char[] d=TeleInPdu.ToCharArray();//存入字符数组 for (int i=0;i<d.Length;i+=2)//字符两两对调 { Tele+=d[i+1].ToString(); Tele+=d[i].ToString(); } Tele=Tele.Substring(0,11);//去掉末位"F" return Tele;//返回发送方电话号码 } //===================================================================================== private string GetTimeFromPdu(string pdu) //解析 DateTime 函数 { //截取PDU串中短信日期时间源码 string TimeInPdu=pdu.Substring(42,12); string Time=""; char[] d=TimeInPdu.ToCharArray();//存入字符数组 for (int i=0;i<d.Length;i+=2)//字符两两对调 { Time+=d[i+1].ToString(); Time+=d[i].ToString(); } Time="20"+Time; //将年份前加 20 形成 4 位格式,以下为日期时间输出格式控制 Time=Time.Substring(0,4)+"-"+Time.Substring(4,2)+"-" +Time.Substring(6,2)+" "+Time.Substring(8,2)+":" +Time.Substring(10,2)+":"+Time.Substring(12,2); return Time; //返回短信日期时间[年-月-日 时:分:秒] } //===================================================================================== private string GetTextFromPdu_UCS2(string pdu) //解析短信正文函数_UCS2编码 { //截取PDU串中短信正文部分源码,读者也可用BitConverter函数实现部分转换 string TextInPdu=pdu.Substring(58); string Text=""; char[] d=TextInPdu.ToCharArray();//存入字符数组 for (int i=0;i<d.Length;i+=4) { int unicode_nu=0; for (int m=0;m<4;m++) //计算 Unicode 十进制值 unicode_nu+=HexToDec(d[i+m])*(1<<((3-m)*4)); Text+=(char)unicode_nu; //输出 Unicode 对应字符 } return Text;//返回短信正文内容 } //===================================================================================== private int HexToDec(char Hex) //16 进制转 10 进制 { int Dec; if(Hex>='0' && Hex<='9') Dec=Convert.ToInt16(Hex-'0'); //0-9 else Dec=Convert.ToInt16(Hex-'A')+10;// A-F return Dec;//返回 10 进制值 } //===================================================================================== //该段内容为作者根据 7-Bit 编码原理图,应用数学矩阵关系式方法导出的解码流程,相信一定有 //更简便的方法, 恳请读者批评指教!! Email: sztcm@public.szptt.net.cn private string GetTextFromPdu_7Bit(string pdu) //解析短信正文函数_7-Bit编码 { string TextInPdu=pdu.Substring(58);//截取PDU串中短信正文部分源码 string Text=""; while (TextInPdu.Length%14!=0) //最后一组不满7个成员时补"0" TextInPdu+="0"; char[] a=TextInPdu.ToCharArray(); //将源码存入字符数组 a[] string b=""; for(int i=0;i<a.Length;i++) //将源码转为二进制并存入字符串 b b+=GetBinary(a[i]); char[] total=b.ToCharArray(); //将二进制码存入字符数组 total[] for ( int j=0;j<total.Length;j+=56) //56位二进制码为一组,循环所有组 { char[] s=new char[56]; for(int i=0;i<56;i++) //将一组二进制码拷贝到字符数组 s[] s[i]=total[i+j]; char[] d=new char[56]; //------------------------------------------------------------------------------------- for(int i=0;i<7;i++) //组内解码得到目标二进制码数组 d[] d[i]=s[i+1]; for(int k=1;k<=6;k++) { for( int i=k*7;i<(k+1)*7-k; i++) d[i]=s[i+(k*2+1)]; for ( int i=(k+1)*7-k;i<(k+1)*7;i++) d[i]=s[i-((7-k)*2+1)]; } for ( int i=49;i<56;i++) d[i]=s[i-1]; //------------------------------------------------------------------------------------- for(int k=0;k<56;k+=7) //组内循环得到目标 ASCII 字符 { int ascii_nu=0; for (int m=0;m<7;m++) ascii_nu+=Convert.ToInt16(d[k+m].ToString())*(1<<(6-m)); Text+=(char)ascii_nu; //输出 ASCII 码相应字符 } } // END OF loop j return Text; } //===================================================================================== private string GetBinary(char Hex) //16 进制转 2 进制 { int Dec; if(Hex>='0' && Hex<='9') Dec=Convert.ToInt16(Hex-'0'); else Dec=Convert.ToInt16(Hex-'A')+10; int displayMask=1<<3; StringBuilder Bin=new StringBuilder(); for(int i=0;i<4;i++) { //append 0 or 1 depending on result of masking Bin.Append( (Dec & displayMask)==0 ? "0":"1" ); //shift left so that mask will find bit of next digit //during next iteration of loop Dec<<=1; } return Bin.ToString(); } //===================================================================================== } // END OF class Form1 } // END OF namespace TC35iSMS
__________________
让世界倾听我们的笛声 |
书签 |
当前查看此主题的会员: 1 (0 位会员和 1 位游客) | |
|
|
相似的主题 | ||||
主题 | 主题作者 | 版面 | 回复 | 最后发表 |
AT指令-通过串口收发短消息(上,下) | admin | 短信开发资料 | 0 | 2012-08-24 18:44 |
用串口连接GSM手机发送和接收短消息,在应用程序中如何编程实现 | admin | 短信开发资料 | 0 | 2012-08-24 15:58 |
编译指令与说明(Delphi) | susu8988 | 短信开发资料 | 0 | 2009-10-26 09:59 |
Delphi中的DLL的编写 | susu8988 | 短信开发资料 | 0 | 2009-10-22 10:38 |
通过串口收发短消息 | lihb | 短信开发资料 | 0 | 2009-10-15 14:53 |