c#实现wavecom短信猫发送长短信
2016-07-11 11:12
441 查看
长短信是有规约的,协议头部分如果是0x40以下,则说明是普通短信,如果是0x40以上,则是长短信,然后在短信内容部分,有六个字节分别定义短信唯一标识以及该短信是第几条,所以长短信发送时每条实际为67个汉字。手机接收到之后,都会按照标准规约自动组合为一条短信,而不是显示多条。
我做了一个AT指令操作wavecom短信猫的类,可以接收和发送超长短信,并且接收到的短信会直接通知电脑。
超长短信:短信内容超过70个汉字,提交给网关时候需要分成多条,但是用户手机接收时候是一条(sp角度,手机发送长短信概念一样)。
在cmpp协议里,CMPP-_SUBMIT消息定义中有相应的参数配置:
TP_udhi :0代表内容体里不含有协议头信息 1代表内容含有协议头信息(长短信,push短信等都是在内容体上含有头内容的)当设置内容体包含协议头,需要根据协议写入相应的信息,长短信协议头有两种:
6位协议头格式:05 00 03 XX MM NN
byte 1 : 05, 表示剩余协议头的长度
byte 2 : 00, 这个值在GSM 03.40规范9.2.3.24.1中规定,表示随后的这批超长短信的标识位长度为1(格式中的XX值)。
byte 3 : 03, 这个值表示剩下短信标识的长度
byte 4 : XX,这批短信的唯一标志(被拆分的多条短信,此值必需一致),事实上,SME(手机或者SP)把消息合并完之后,就重新记录,所以这个标志是否唯
一并不是很 重要。
byte 5 : MM, 这批短信的数量。如果一个超长短信总共5条,这里的值就是5。
byte 6 : NN, 这批短信的数量。如果当前短信是这批短信中的第一条的值是1,第二条的值是2。
例如:05 00 03 39 02 01
7位的协议头格式:06 08 04 XX XX MM NN byte 1 : 06, 表示剩余协议头的长度
byte 2 : 08, 这个值在GSM 03.40规范9.2.3.24.1中规定,表示随后的这批超长短信的标识位长度为2(格式中的XX值)。
byte 3 : 04, 这个值表示剩下短信标识的长度
byte 4-5 : XX XX,这批短信的唯一标志,事实上,SME(手机或者SP)把消息合并完之后,就重新记录,所以这个标志是否唯一并不是很重要。
byte 6 : MM, 这批短信的数量。如果一个超长短信总共5条,这里的值就是5。
byte 7 : NN, 这批短信的数量。如果当前短信是这批短信中的第一条的值是1,第二条的值是2。
例如:06 08 04 00 39 02 01
到此,长短信的发送设置基本完成,但是有一点要注意:Src_Id 协议里这个字段在一条长短信中必须要一样,不然手机会解析成三条,
并三条都 是错误短信。
对于sp来说,长短信上行,按照协议反过来解析:
1byte[] contentBytes = msg.getMsgContent();
2int headLen = contentBytes[0]; // 内容头的长度
3// 超长短信总条数
4int pk_total = contentBytes[headLen - 1];
5// 超长短信第几条
6int pk_num = contentBytes[headLen];
7// 超长短信序号
8byte serial = contentBytes[headLen - 2];
我做了一个AT指令操作wavecom短信猫的类,可以接收和发送超长短信,并且接收到的短信会直接通知电脑。
超长短信:短信内容超过70个汉字,提交给网关时候需要分成多条,但是用户手机接收时候是一条(sp角度,手机发送长短信概念一样)。
在cmpp协议里,CMPP-_SUBMIT消息定义中有相应的参数配置:
TP_udhi :0代表内容体里不含有协议头信息 1代表内容含有协议头信息(长短信,push短信等都是在内容体上含有头内容的)当设置内容体包含协议头,需要根据协议写入相应的信息,长短信协议头有两种:
6位协议头格式:05 00 03 XX MM NN
byte 1 : 05, 表示剩余协议头的长度
byte 2 : 00, 这个值在GSM 03.40规范9.2.3.24.1中规定,表示随后的这批超长短信的标识位长度为1(格式中的XX值)。
byte 3 : 03, 这个值表示剩下短信标识的长度
byte 4 : XX,这批短信的唯一标志(被拆分的多条短信,此值必需一致),事实上,SME(手机或者SP)把消息合并完之后,就重新记录,所以这个标志是否唯
一并不是很 重要。
byte 5 : MM, 这批短信的数量。如果一个超长短信总共5条,这里的值就是5。
byte 6 : NN, 这批短信的数量。如果当前短信是这批短信中的第一条的值是1,第二条的值是2。
例如:05 00 03 39 02 01
7位的协议头格式:06 08 04 XX XX MM NN byte 1 : 06, 表示剩余协议头的长度
byte 2 : 08, 这个值在GSM 03.40规范9.2.3.24.1中规定,表示随后的这批超长短信的标识位长度为2(格式中的XX值)。
byte 3 : 04, 这个值表示剩下短信标识的长度
byte 4-5 : XX XX,这批短信的唯一标志,事实上,SME(手机或者SP)把消息合并完之后,就重新记录,所以这个标志是否唯一并不是很重要。
byte 6 : MM, 这批短信的数量。如果一个超长短信总共5条,这里的值就是5。
byte 7 : NN, 这批短信的数量。如果当前短信是这批短信中的第一条的值是1,第二条的值是2。
例如:06 08 04 00 39 02 01
到此,长短信的发送设置基本完成,但是有一点要注意:Src_Id 协议里这个字段在一条长短信中必须要一样,不然手机会解析成三条,
并三条都 是错误短信。
对于sp来说,长短信上行,按照协议反过来解析:
1byte[] contentBytes = msg.getMsgContent();
2int headLen = contentBytes[0]; // 内容头的长度
3// 超长短信总条数
4int pk_total = contentBytes[headLen - 1];
5// 超长短信第几条
6int pk_num = contentBytes[headLen];
7// 超长短信序号
8byte serial = contentBytes[headLen - 2];
class DuanXin { public string phnum; public string message; public DuanXin() { } public DuanXin(string ph, string msg) { phnum = ph; message = msg; } } class CDuanXin { public byte biaozhi; public byte tiaoshu; public byte dqtiaoshu; public string dianhua; public string msg; public DateTime datetime; public CDuanXin(byte bz, byte ts, byte dqts, string dh, string mg, DateTime dt) { biaozhi = bz; tiaoshu = ts; dqtiaoshu = dqts; dianhua = dh; msg = mg; datetime = dt; } } public class WaveComMsg { public int Port; public int error; readonly string zhongzhi = new string((char)26, 1); const string head = "00"; const string quyu = "000D9168"; const string bianma = "000801"; const string shujutou = "050003"; const string ddx = "11"; const string cdx = "55"; StringBuilder fszifu = new StringBuilder(350); Queue<DuanXin> duanxins = new Queue<DuanXin>(60); Object listobj = new Object(); List<string> items = new List<string>(8); public Action<int, string, string, int> Fsjieguo = null; public Action<string, string, DateTime> RcvMsg = null; public Action DuQu = null; bool kongxian = true; bool duqu = true; Action<DuanXin> Sendmsg = null; Random rd = new Random(); SerialPort sp = null; List<CDuanXin> recvcd = new List<CDuanXin>(20); public WaveComMsg(int port) { Port = port; sp = new SerialPort("COM"+port); sp.RtsEnable = true; sp.DtrEnable = true; DuQu = ksduqu; sp.Open(); Sendmsg = sendmessage; } public bool Chushihua() { string ss = string.Empty; try { sp.Write("AT+CMGF=0" + "\r"); while (true) { ss = sp.ReadLine(); if (ss.Contains("OK")) break; else if (ss.Contains("ERROR")) return false; } } catch { return false; } try { sp.Write("AT+CNMI=2,2,0,0,1" + "\r"); while (true) { ss = sp.ReadLine(); if (ss.Contains("OK")) { sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived); return true; } else if (ss.Contains("ERROR")) return false; } } catch { return false; } } public void DoWork(string phnumber, string msg) { DuanXin dx = new DuanXin(phnumber, msg); bool busy = false; lock (listobj) { if (duanxins.Count > 0) busy = true; duanxins.Enqueue(dx); } if (!busy) Sendmsg.BeginInvoke(dx, null, null); } void sendmessage(DuanXin dx) { byte[] msgs = Encoding.BigEndianUnicode.GetBytes(dx.message); if (dx.message.Length <= 70) { fszifu.Append(head); fszifu.Append(ddx); fszifu.Append(quyu); fszifu.Append(phonedecode(dx.phnum)); fszifu.Append(bianma); fszifu.Append(Convert.ToString((dx.message.Length) * 2, 16).PadLeft(2, '0')); for (int i = 0; i < msgs.Length; i++) fszifu.Append(Convert.ToString(msgs[i], 16).PadLeft(2, '0')); fszifu.Append(zhongzhi); items.Add(fszifu.ToString()); fszifu.Clear(); } else { string h = Convert.ToString(rd.Next(1, 127), 16); int lnum = dx.message.Length / 67 + 1; for (int i = 0; i < lnum; i++) { int sjl = i + 1 == lnum ? msgs.Length % 134 + 6 : 140; fszifu.Append(head); fszifu.Append(cdx); fszifu.Append(quyu); fszifu.Append(phonedecode(dx.phnum)); fszifu.Append(bianma); fszifu.Append(Convert.ToString(sjl, 16).PadLeft(2, '0')); fszifu.Append(shujutou); fszifu.Append(h.PadLeft(2, '0')); fszifu.Append(Convert.ToString(lnum, 16).PadLeft(2, '0')); fszifu.Append(Convert.ToString(i + 1, 16).PadLeft(2, '0')); for (int x = 0; x < sjl - 6; x++) fszifu.Append(Convert.ToString(msgs[i * 134 + x], 16).PadLeft(2, '0')); fszifu.Append(zhongzhi); items.Add(fszifu.ToString()); fszifu.Clear(); } } string ss; int success = 0; kongxian = false; for (int m = 0; m < items.Count; m++) { try { sp.Write("AT+CMGS=" + (items[m].Length / 2 - 1).ToString().PadLeft(3, '0') + "\r"); sp.Write(items[m]); while (true) { ss = sp.ReadLine(); if (ss.StartsWith("0891")) readmsg(ref ss); else if (ss.Contains("OK")) { success = 1; break; } else if (ss.Contains("ERROR")) { success = 0; break; } } } catch { success = 0; } } kongxian = true; if (success == 0) error++; if (Fsjieguo != null) Fsjieguo.BeginInvoke(Port, dx.phnum, dx.message, success, null, null); items.Clear(); bool busy = false; lock (listobj) { duanxins.Dequeue(); if (duanxins.Count > 0) busy = true; } if (busy) Sendmsg.BeginInvoke(duanxins.Peek(), null, null); } unsafe string phonedecode(string ph) { int x = ph.Length % 2 == 0 ? ph.Length : ph.Length + 1; char* ca = stackalloc char[x]; for (int i = 0; i < x; i++) if (i % 2 == 0) *(ca + i) = (i + 1) == ph.Length ? 'F' : ph[i + 1]; else *(ca + i) = ph[i - 1]; return new string(ca, 0, x); } unsafe string phoneencode(string ph) { int x = ph.Length; char* ca = stackalloc char[x]; for (int i = 0; i < x; i++) if (i % 2 == 0) *(ca + i) = ph[i + 1]; else *(ca + i) = ph[i - 1]; if (*(ca + x - 1) == 'F') x--; return new string(ca, 0, x); } unsafe DateTime fsshijian(string sj) { DateTime dt = new DateTime(); if (sj.Length == 10) { char* ca = stackalloc char[16]; ca[0] = '2'; ca[1] = '0'; ca[2] = sj[1]; ca[3] = sj[0]; ca[4] = '/'; ca[5] = sj[3]; ca[6] = sj[2]; ca[7] = '/'; ca[8] = sj[5]; ca[9] = sj[4]; ca[10] = ' '; ca[11] = sj[7]; ca[12] = sj[6]; ca[13] = ':'; ca[14] = sj[9]; ca[15] = sj[8]; DateTime.TryParse(new string(ca, 0, 16), out dt); } return dt; } unsafe void ksduqu() { bool ydx = false; string s = string.Empty; while (true) { try { s = sp.ReadLine(); } catch { ydx = false; break; } if (s.StartsWith("0891")) { ydx = true; break; } } duqu = true; if (ydx) readmsg(ref s); }
void readmsg(ref string s) { s = s.Trim(); string mmsg = string.Empty; int cd = Convert.ToInt32(s.Substring(18, 2), 16); int hmcd = Convert.ToInt32(s.Substring(20, 2), 16); if (hmcd % 2 == 1) hmcd++; string phonum = phoneencode(s.Substring(24, hmcd)); string bm = s.Substring(24 + hmcd, 4); DateTime fssj = fsshijian(s.Substring(28 + hmcd, 10)); int len = Convert.ToByte(s.Substring(42 + hmcd, 2), 16); if (cd < 64) { byte[] b = new byte[len]; if (bm == "0000")//text { byte y = 0; int l = 0; for (int t = 0; t < len; t++) { if (t % 8 == 7) { l++; b[t] = y; y = 0; } else { byte x = Convert.ToByte(s.Substring(2 * (t - l) + 44 + hmcd, 2), 16); b[t] = (byte)((((byte)(x << ((t - l) % 7) + 1)) >> 1) + y); y = (byte)(x >> (7 - ((t - l) % 7))); } } mmsg = Encoding.ASCII.GetString(b); } else if (bm == "0008")//tpdu { for (int i = 0; i < len; i++) b[i] = Convert.ToByte(s.Substring(44 + hmcd + i * 2, 2), 16); mmsg = Encoding.BigEndianUnicode.GetString(b); } if (RcvMsg != null) RcvMsg.BeginInvoke(phonum, mmsg, fssj, null, null); } else { byte[] b = new byte[len]; if (bm == "0000")//text { byte y = 0; int l = 0; for (int t = 0; t < len; t++) { if (t % 8 == 7) { l++; b[t] = y; y = 0; } else { byte x = Convert.ToByte(s.Substring(2 * (t - l) + 44 + hmcd, 2), 16); b[t] = (byte)((((byte)(x << ((t - l) % 7) + 1)) >> 1) + y); y = (byte)(x >> (7 - ((t - l) % 7))); } } mmsg = Encoding.ASCII.GetString(b, 7, len - 7); } else if (bm == "0008")//tpdu { for (int i = 0; i < len - 6; i++) b[i] = Convert.ToByte(s.Substring(56 + hmcd + i * 2, 2), 16); mmsg = Encoding.BigEndianUnicode.GetString(b, 0, len - 6); } byte bz = Convert.ToByte(s.Substring(50 + hmcd, 2), 16); byte ts = Convert.ToByte(s.Substring(52 + hmcd, 2), 16); byte dqts = Convert.ToByte(s.Substring(54 + hmcd, 2), 16); recvcd.Add(new CDuanXin(bz, ts, dqts, phonum, mmsg, DateTime.Now)); var cx = (from c in recvcd where c.biaozhi == bz && c.dianhua == phonum orderby c.dqtiaoshu select c).Distinct(); if (cx.Count() == ts) { string hjmsg = null; foreach (var m in cx) { hjmsg += m.msg; recvcd.Remove(m); } if (RcvMsg != null) RcvMsg.BeginInvoke(phonum, hjmsg, fssj, null, null); } var ccx = from c in recvcd where (DateTime.Now - c.datetime).Minutes > 30 select c; foreach (var n in ccx) recvcd.Remove(n); } } void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) { if (kongxian) { if (duqu) { duqu = false; DuQu.BeginInvoke(null, null); } } } public void release() { sp.Close(); } }
相关文章推荐
- 纯英文长短信编码
- 短信网关协议(cmpp、sgip、smgp、smpp)长短信开发要点
- C# 中DateTime的各种使用
- c# UDP通信 列子
- c# UDP通信
- C#深入浅出全接触(二)
- C#深入浅出全接触(一)
- C#中MD5 - 16/32位加密实例
- c#新手入门级代码分享 using指令与using语句的实例
- 在C# project中添加引用的System.Configuration的过程:
- 再谈网游同步技术:实时动作游戏同步方式和传输协议选择
- c#中Mschart的series中的label的值
- 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq
- C#高级编程阅读笔记-1
- [学习笔记]C# Socket初试-服务端
- 《C#高级编程》读书笔记(八):LINQ
- 异常处理基础(C#参考)
- C#异步操作
- C#基础知识(五)——常量、枚举、结构体、数组
- 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel