短信猫软件的实现(C#)超长短信 您所在的位置:网站首页 多人短信能发合并一条短信 短信猫软件的实现(C#)超长短信

短信猫软件的实现(C#)超长短信

2024-07-06 04:54| 来源: 网络整理| 查看: 265

超长短信:长度超过一条,而分多条发送的短信,通过用户数据头标识在接收端进行组合的短信(接收的短信在手机或其他终端上看到的是一条)。GSM_03.40规范中是Concatenated Short Messages :This facility allows short messages to be concatenated to form a longer message. 此种短信理论上最长可以将255条短信合成一条,名副其实的超长短信。

有关超长短信可以参考GSM_03.40规范和CMPP有关超长短信的内容:GSM_03.40规范中的 9.2.3.23 TP-User-Data-Header-Indicator (TP-UDHI) 和9.2.3.24 TP-User Data (TP-UD) 本文的程序是在原来基础上添加的,详细请参考:短信猫软件的实现(C#)系列博客索引

PDU字符串中与超长短信有关的只有TP-UDHI位(在PDU字串中的PDUType的D6位),有关PDU编码请参考:短信猫软件的实现(C#)PDU格式短信解析。TP-UDHI位为1,则在User Data中含有消息头,用来表示各种不同的其他形式短信,其中包括长短信。

消息头是User Data的开头部分,有两种格式:6位格式和 7位格式。6位:05 00 03 XX MM NN;7位格式:06 08 04 XX XX MM NN。

各字节含义:byte 1:剩余协议头长度。byte 2:00/08 这个字节在GSM 03.40规范9.2.3.24中规定,00:代表长短信,8位参考标识;08:代表长短信,16位参考标识;还规定了其他数值,与长短信无关,详细参考GSM 03.40规范9.2.3.24。byte 3:代表剩下短信标识的长度:03,三个字节;04,四个字节。byte 4:XX 这批短信的唯一标志,事实上,SME(手机或者SP)把消息合并完之后,就重新记录,所以这个标志是否唯一并不是很 重要。7位格式的 和byte 5一起作为16位标志。byte 5:MM 这批短信的数量,超长短信分成几条,值即是几。7位 XX和byte 4共同作为16位标识。byte 6:NN 本条短信在超长短信中是第几条。7位格式 MM 同6位格式的 MM。byte 7:NN 7位格式中,同6位格式中的NN。

长短信消息头规律:第一个字节:消息头剩余长度;第二字节:消息类型;第三字节:剩余消息头长度;后面一个或两个字节根据标识位数作为这批短信的唯一标识,是否唯一不重要,但同批短信标志位必须相同,否则将被解析成多条短信。后面两个字节分别是总数量和序号。

编码实现:

此次编码是在之前编码基础上添加长短信编解码部分而实现的,添加时不对原来程序做过多修改;这次添加长短信深感这个类库的可扩展性太差,以致程序有点乱,添加长短信费了一番功夫,而且功能实现不尽合理;由于这段时间比较忙,暂时不对程序做大的改动,仅仅添加长短信编码部分。

对编解码类的更改:

属性更改:

长短信发送时需将TP-UDHI位置为1,而这位位于PDU-type 这个8位组,普通短信这个八位组发送时值为“11” 接收时为“24”,长短信 分别为: “51”、“64”。之前程序对应的属性只能读到“11”,字段值也为“11”没有更改。为使其支持长短信编解码将其中属性、字段更改为:

1: private string protocolDataUnitType = "11"; 2: /// 3: /// 协议数据单元类型(1个8位组) 4: /// 5: public string ProtocolDataUnitType 6: { 7: set 8: { 9: protocolDataUnitType = value; 10: } 11: get 12: { 13: return protocolDataUnitType; 14: } 15: }

这样编解码时只需正确设置属性值,即可完成长短信的编解码。

方法更改:

编码:(USC2/7位):

只需把原来程序 字符数超过最大字符数时 抛出异常改为 对应长短信编码即可;为了改动的地方比较少,返回值:长短信返回逗号分隔的PDU串。7bit编码须做一定处理,规范中要求添加填充位,让后面userData符合7bit的格式;6byte消息头共占48bit 填充一位补成49bit,相当于后面第一个ASCII符做一定特殊处理,后面直接调用之前的编码函数即可,通过验证发现 第一个只需左移一位,即完成这一位编码,放入PDU传即可。

1: /// 2: /// PDU编码器,完成PDU编码(USC2编码,超过70个字时 分多条发送,PDU各个串之间逗号分隔) 3: /// 4: /// 目的手机号码 5: /// 短信内容 6: /// 编码后的PDU字符串 长短信时 逗号分隔 7: public string PDUUSC2Encoder(string phone, string Text) 8: { 9: DestinationAddress = phone; 10:   11: if (Text.Length > 70) 12: { 13: //长短信设TP-UDHI位为1 PDU-type = “51” 14: ProtocolDataUnitType = "51"; 15:   16: //计算长短信条数 17: int count = Text.Length / 67 + 1; 18:   19: //长短信格式字符串,格式 每条之间 逗号分隔 20: string result = string.Empty; 21:   22: for (int i = 0; i < count; i++) 23: { 24: //如果不是最后一条 25: if (i != count - 1) 26: { 27: UserData = Text.Substring(i * 67, 67); 28:   29: result += serviceCenterAddress + protocolDataUnitType 30: + messageReference + destinationAddress + protocolIdentifer 31: + dataCodingScheme + validityPeriod + (userData.Length / 2 + 6).ToString("X2") 32: + "05000339" + count.ToString("X2") + (i + 1).ToString("X2") + userData + ","; 33: } 34: else 35: { 36: UserData = Text.Substring(i * 67); 37:   38: if (userData != null || userData.Length != 0) 39: { 40:   41: result += serviceCenterAddress + protocolDataUnitType 42: + messageReference + destinationAddress + protocolIdentifer 43: + dataCodingScheme + validityPeriod + (userData.Length / 2 + 6).ToString("X2") 44: + "05000339" + count.ToString("X2") + (i + 1).ToString("X2") + userData; 45: } 46: else 47: { 48: result = result.TrimEnd(','); 49: } 50: } 51: } 52:   53: return result; 54: } 55:   56: //不是长短信 57: UserData = Text; 58: return serviceCenterAddress + protocolDataUnitType 59: + messageReference + destinationAddress + protocolIdentifer 60: + dataCodingScheme + validityPeriod + userDataLenghth + userData; 61: } 62:   63: /// 64: /// 7bit 编码(超过160个字时 分多条发送,PDU各个串之间逗号分隔) 65: /// 66: /// 手机号码 67: /// 短信内容 68: /// 编码后的字符串 长短信时 逗号分隔 69: public string PDU7BitEncoder(string phone, string Text) 70: { 71: dataCodingScheme = "00"; 72: DestinationAddress = phone; 73:   74: if (Text.Length > 160) 75: { 76: //长短信设TP-UDHI位为1 PDU-type = “51” 77: ProtocolDataUnitType = "51"; 78:   79: //计算长短信条数 80: int count = Text.Length / 153 + 1; 81:   82: //长短信格式字符串,格式 每条之间 逗号分隔 83: string result = string.Empty; 84:   85: for (int i = 0; i < count; i++) 86: { 87: //如果不是最后一条 88: if (i != count - 1) 89: { 90: UserData = Text.Substring(i * 153 + 1, 152); 91:   92: result += serviceCenterAddress + protocolDataUnitType 93: + messageReference + destinationAddress + protocolIdentifer 94: + dataCodingScheme + validityPeriod + (160).ToString("X2") 95: + "05000339" + count.ToString("X2") + (i + 1).ToString("X2") 96: +((int)(new ASCIIEncoding().GetBytes(Text.Substring(i*153,1))[0]> 1); 47:   48: return strPDU.Substring(lenSCA + lenOA + 22 + 4 * 2, 2 * 2) 49: + strPDU.Substring(lenSCA + lenOA + 22 + 3 * 2, 2) + "," + ServiceCenterAddress + "," 50: + OriginatorAddress + "," + ServiceCenterTimeStamp + "," + first + UserData; 51: } 52: } 53:   54: userData = strPDU.Substring(lenSCA + lenOA + 22); 55: return "010100," + ServiceCenterAddress + "," + OriginatorAddress + "," + ServiceCenterTimeStamp + "," + UserData; 56: }

这样,程序返回值字符串中有长短信的有关消息,前两个8位组 分别指示这批短信的总条数和这条所在的序号,调用方只需对这两个8位组和这批短信的唯一标识判断处理即可解码出长短信,拼接长短信。

注释掉:public void PDUDecoder(string strPDU, out string msgCenter, out string phone, out string msg, out string time)方法。对应对其的调用都改为对刚修改的函数的调用。

2 . GSMMODEM类更改:

接收短信,读取短信先读出刚才信息字符串(MMNN,中心号码,手机号码,发送时间,短信内容 MM这批短信总条数 NN本条所在序号)然后在处理(现在程序对此不做处理,需要的话 自己先改动)

发送短信:只需添加长短信的编码的发送,用foreach语句遍历发送各条PDU串即可:

1: /// 2: /// 发送短信 3: /// 发送失败将引发异常 4: /// 5: /// 手机号码 6: /// 短信内容 7: public void SendMsg(string phone, string msg) 8: { 9: PDUEncoding pe = new PDUEncoding(); 10: pe.ServiceCenterAddress = msgCenter; //短信中心号码 服务中心地址 11: 12: string temp = pe.PDUUSC2Encoder(phone, msg); 13:   14: string tmp = string.Empty; 15:   16: foreach (string str in temp.Split(',')) 17: { 18: int len = (str.Length - Convert.ToInt32(str.Substring(0, 2), 16) * 2 - 2) / 2; //计算长度 19: 20: try 21: { 22: //注销事件关联,为发送做准备 23: sp.DataReceived -= sp_DataReceived; 24:   25: sp.Write("AT+CMGS=" + len.ToString() + "\r"); 26: sp.ReadTo(">"); 27: sp.DiscardInBuffer(); 28:   29: //事件重新绑定 正常监视串口数据 30: sp.DataReceived += sp_DataReceived; 31:   32: tmp = SendAT(str + (char)(26)); //26 Ctrl+Z ascii码 33: } 34: catch (Exception) 35: { 36: throw new Exception("短信发送失败"); 37: } 38: finally 39: { 40: } 41:   42: if (tmp.Substring(tmp.Length - 4, 3).Trim() == "OK") 43: { 44: continue; 45: } 46:   47: throw new Exception("短信发送失败"); 48: } 49: } 50:   51: /// 52: /// 发送短信 (重载) 53: /// 54: /// 手机号码 55: /// 短信内容 56: /// 短信类型 57: public void SendMsg(string phone, string msg, MsgType msgType) 58: { 59: if (msgType == MsgType.AUSC2) 60: { 61: SendMsg(phone, msg); 62: } 63: else 64: { 65:   66: PDUEncoding pe = new PDUEncoding(); 67: pe.ServiceCenterAddress = msgCenter; //短信中心号码 服务中心地址 68: 69: string temp = pe.PDU7BitEncoder(phone, msg); 70:   71: string tmp = string.Empty; 72:   73: foreach (string str in temp.Split(',')) 74: { 75:   76: int len = (str.Length - Convert.ToInt32(str.Substring(0, 2), 16) * 2 - 2) / 2; //计算长度 77: try 78: { 79: tmp = SendAT("AT+CMGS=" + len.ToString() + "\r" + str + (char)(26)); //26 Ctrl+Z ascii码 80: } 81: catch (Exception) 82: { 83: throw new Exception("短信发送失败"); 84: } 85:   86: if (tmp.Substring(tmp.Length - 4, 3).Trim() == "OK") 87: { 88: continue; 89: } 90:   91: throw new Exception("短信发送失败"); 92: } 93: } 94: }

直接调用函数即可发送,长度超过最大字符数,自动以长短信发送;调用方式和以前完全一样。

读取短信的函数直接调用解码函数,返回格式同解码函数,调用时需要根据字符串自己组合短信,函数没有太大变化,这里不再给出具体函数了,详细参考附件源程序。

总结:长短信的发送就是把超过协议最大长度的短信分成多条发送,在接收终端(如手机)端看到的是一条短信。置TP-udhi位为1,添加消息头;USC2的编码只需添加消息头,剩下的134个字节可以发送67个字符,7位短信需要加上填充位 6byte消息头占48位,需添加一位填充(0或1)填充位置在本字节的最低位,我的程序把字节左移一位(相当于填充0);接收解码时只需右移一位即可。

附件:工程项目文件



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有