//======================================================================
// 《31天学会CRM项目开发》机械工业出版社 版权所有 (C) 2015-2016
// 文件名:Form1.cs
//E:\WGY\C#学习\一套开源的企业业务系统框架Winform\586570 31天学会CRM项目开发pdf+源代码\31天学会CRM项目开发-源代码\003 实例及拓展练习\SMSDemo
// 随书附赠源代码,若转发需保留版权信息。
// 社区支持:http://www.huiyaosoft.com/
//======================================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Text.RegularExpressions;
using System.Collections;
namespace SMSDemo
{
/*
TEXT格式只能发送英文和数字,若要发送中文,则必须使用PDU格式。
*/
public partial class Form1 : Form
{
private SerialPort serialPort1 = new SerialPort();
private StringBuilder builder = new StringBuilder();
private StringBuilder answer = new StringBuilder();
private String str = string.Empty;
private Boolean isLock = false;
private ArrayList waiteSendMsgList;
// 短信中心号码
private string smsc;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//string type = str3.Substring(40, 2);
//MessageBox.Show(Convert.ToString(14, 16));
//try
//{
// if (type.Equals("00"))
// MessageBox.Show(GetTextFromPdu_7Bit(str3));
// else if (type.Equals("08"))
// MessageBox.Show(GetTextFromPdu_UCS2(str3));
//}
//catch (Exception ex)
//{
// Console.WriteLine(ex.Message);
//}
smsc = getPhoneNum("8613800532500");
waiteSendMsgList = new ArrayList();
// 取得计算机上所有串口
string[] ports = SerialPort.GetPortNames();
Array.Sort(ports);
this.cbComList.Items.AddRange(ports);
this.cbComList.SelectedIndex = this.cbComList.Items.Count > 0 ? 0 : -1;
//初始化SerialPort对象
serialPort1.NewLine = "\r\n";
// 确定是否使 Request To Send (RTS) 线有效。一般情况下,由计算机发送 Request To Send 信号到联接的调制解调器,以请示允许发送数据。
// 当 RTSEnable 设置为 True,端口打开时,Request To Send 线设置为高电平,端口关闭时,设置为低电平。
// Request To Send 线用在 RTS/CTS 硬件握手。RTSEnable 属性允许手动检测 Request To Send 线以确定其状态。
serialPort1.RtsEnable = true;
//添加事件注册
serialPort1.DataReceived += serialPort1_DataReceived;
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致
int n = serialPort1.BytesToRead;
//声明一个临时数组存储当前来的串口数据
byte[] buf = new byte[n];
//读取缓冲数据
serialPort1.Read(buf, 0, n);
//清除字符串构造器的内容
builder = new StringBuilder();
//因为要访问ui资源,所以需要使用invoke方式同步ui。
this.Invoke((EventHandler)(delegate
{
// 直接按ASCII规则转换成字符串
string bufStr = Encoding.ASCII.GetString(buf);
builder.Append(bufStr);
this.tbInfo.AppendText(bufStr);
answer.Append(bufStr);
if (!str.Equals(answer.ToString()))
{
str = answer.ToString();
if (str.IndexOf("OK") > -1
|| str.IndexOf("ERROR") > -1
|| str.IndexOf("BUSY") > -1
|| str.IndexOf("NO CARRIER") > -1
|| str.IndexOf("RING") > -1
|| str.IndexOf("+CMTI") > -1)//发送AT+CMGF=0指令,若收到这些字符串,说明串口处于空闲状态,可写入数据
{
// 重置属性
this.tbInfo.AppendText("\r\n");
this.btnSend.Enabled = true;
answer = new StringBuilder();
isLock = false;
#region 读取短信
if (str.IndexOf("+CMTI") > -1) //若收到+CMTI字符表示收到新短信。
{
isLock = true;
serialPort1.ReadExisting();
serialPort1.WriteLine("AT+CMGL=0"); //可使用此指令读取新短信。
}
try
{
//08 指短信中心号码长度
//91指短信中心号码类型
string reg = @"\r\n0891(\w+)\r\n";
Regex r = new Regex(reg);
if (r.IsMatch(str))
{
string content = r.Match(str).Groups[1].Value.ToString();
content = "0891" + content;
this.tbInfo.AppendText("来自手机:" + GetTeleFromPdu(content) + "\r\n");
this.tbInfo.AppendText("接收时间:" + GetTimeFromPdu(content) + "\r\n");
string type = content.Substring(40, 2);
if (type.Equals("00"))
{
this.tbInfo.AppendText("收到短信:" + GetTextFromPdu_7Bit(content) + "\r\n");
}
else if (type.Equals("08"))
{
this.tbInfo.AppendText("收到短信:" + GetTextFromPdu_UCS2(content) + "\r\n");
}
}
else
this.tbInfo.AppendText("\r\n");
}
catch (Exception ex)
{
this.tbInfo.AppendText(ex.Message + "\r\n");
}
#endregion
// 发送短信
SendMsgBySerialPort();
this.tbInfo.Text = this.tbInfo.Text.Replace("\r\n\r\n","\r\n");
}
}
}));
}
private void btnConn_Click(object sender, EventArgs e)
{
this.tbInfo.Text = string.Empty;
answer = new StringBuilder();
//根据当前串口对象,来判断操作
if (serialPort1.IsOpen)
{
//打开时点击,则关闭串口
serialPort1.Close();
this.btnSend.Enabled = false;
isLock = true;
this.btnConn.Text = "Open";
this.tsStatus.Text = "串口已关闭";
}
else
{
//关闭时点击,则设置好端口,波特率后打开
serialPort1.PortName = this.cbComList.Text;
serialPort1.BaudRate = int.Parse(this.tbRate.Text);
serialPort1.DataBits = 8; //数据位
serialPort1.Parity = System.IO.Ports.Parity.None; //校验位 无校验
serialPort1.StopBits = System.IO.Ports.StopBits.One; //停止位1位
serialPort1.ReadBufferSize = 1024;
try
{
serialPort1.Open();
serialPort1.ReadTimeout = 1000;
serialPort1.WriteTimeout = 500;
serialPort1.WriteLine("AT+CMGF=0");
// serialPort1.WriteLine("AT+CMGD=1,2"); // 删除短信
this.btnSend.Enabled = true;
isLock = false;
this.btnConn.Text = "Close";
this.tsStatus.Text = "串口已连接";
}
catch (Exception ex)
{
//捕获到异常信息,创建一个新的serialPort1对象,之前的不能用了。
serialPort1 = new SerialPort();
this.tbInfo.AppendText(ex.Message.ToString()+"\r\n");
}
}
this.btnSend.Enabled = serialPort1.IsOpen;
}
private void btnSend_Click(object sender, EventArgs e)
{
if (!isLock)//串口未被使用
{
isLock = true;
this.btnSend.Enabled = false;
serialPort1.ReadExisting();
serialPort1.WriteLine(this.tbCmd.Text);
}
}
#region send msg
/// <summary>
/// 电话号码奇偶位对调
/// </summary> 发送PDU字符串格式
/// <param name="num"></param>
/// <returns></returns>
public static string getPhoneNum(string num)
{
//8613800532500
if (num.Length == 13)
{
num = num + "F"; //加个F补齐偶数长度
string newnum = string.Empty;
newnum = num.Substring(1, 1) + num.Substring(0, 1) + num.Substring(3, 1) + num.Substring(2, 1) + num.Substring(5, 1) + num.Substring(4, 1) + num.Substring(7, 1) + num.Substring(6, 1) + num.Substring(9, 1) + num.Substring(8, 1) + num.Substring(11, 1) + num.Substring(10, 1) + num.Substring(13, 1) + num.Substring(12, 1);
return newnum;
}
else if (num.Length == 15)
{
num = num + "F";//加个F补齐偶数长度
string newnum = string.Empty;
newnum = num.Substring(1, 1) + num.Substring(0, 1) + num.Substring(3, 1) + num.Substring(2, 1) + num.Substring(5, 1) + num.Substring(4, 1) + num.Substring(7, 1) + num.Substring(6, 1) + num.Substring(9, 1) + num.Substring(8, 1) + num.Substring(11, 1) + num.Substring(10, 1) + num.Substring(13, 1) + num.Substring(12, 1) + num.Substring(15, 1) + num.Substring(14, 1);
return newnum;
}
else
{
return string.Empty;
}
}
/// <summary>
/// 对字符串加码
/// </summary>
/// <param name="str"></param>
/// <param name="getBytes"></param>
/// <returns></returns>
public static string StrToBin(string str)
{
str = str.Replace("\u0000", string.Empty);
byte[] charBin = Encoding.BigEndianUnicode.GetBytes(str);
string result = string.Empty;
foreach (byte hexNum in charBin)
{
result += hexNum.ToString("X2");
}
return result;
}
public static string BinToStr(string str)
{
string result = string.Empty;
for (int i = 0; i < str.Length / 2; i++)
{
result += (char)short.Parse(str.Substring(i * 2, 2), global::System.Globalization.NumberStyles.HexNumber);
}
return result;
}
public static string getHex(Int32 l)
{
try
{
string s = Convert.ToString(l, 16);
if (s.Length == 1)
{
return "0" + s;
}
else
{
return s;
}
}
catch (Exception ex)
{
return ex.ToString();
}
}
private void SendMsg(String phoneNum, String content)
{
try
{
//ATD86811596;拨电话
//ATH挂电话
//网通8613090175500,移动8613800544500。注意加上86
string to = getPhoneNum("86" + phoneNum);//接收号码
StringBuilder msgBuilder = new StringBuilder();
foreach (char s in content)
{
msgBuilder.Append(StrToBin(s.ToString()));
if (msgBuilder.ToString().Length >= 270)
{
// 加入短信队列
AddToWaiteSendMsgList(smsc, to, msgBuilder.ToString());
msgBuilder = new StringBuilder();
}
}
if (!string.IsNullOrEmpty(msgBuilder.ToString()))
AddToWaiteSendMsgList(smsc, to, msgBuilder.ToString());
this.tbInfo.AppendText("正在发送短信:" + phoneNum + "," + content + "\r\n");
SendMsgBySerialPort();
}
catch (Exception ex)
{
this.tbInfo.AppendText(ex.Message + "\r\n");
}
}
private void AddToWaiteSendMsgList(string smsc_, string to, string pduContent)
{
// 拼接PDU命令
int l = pduContent.Length / 2;
answer = new StringBuilder();
pduContent = "11000D91" + to + "000800" + getHex(l) + pduContent;
pduContent = "0891" + smsc_ + pduContent;
pduContent = pduContent.ToUpper();
l = (pduContent.Length - 18) / 2;
waiteSendMsgList.Add("AT+CMGS=" + l.ToString() + "\r" + pduContent);
}
private void SendMsgBySerialPort()
{
#region 发送短信
if (waiteSendMsgList.Count > 0)
{
isLock = true;
this.serialPort1.Write(waiteSendMsgList[0].ToString());
this.serialPort1.Write("\x01A");
//this.serialPort1.WriteLine(string.Empty);
//System.Threading.Thread.Sleep(100);
this.tbInfo.AppendText(string.Format("正在发送……,还有{0}条等待\r\n", (waiteSendMsgList.Count -1)));
waiteSendMsgList.RemoveAt(0);
}
#endregion
}
private string GetTextFromPdu_7Bit(string pdu) //解析短信正文函数_7-Bit编码
{
string TextInPdu = pdu.Substring(58);//截取PDU串中短信正文部分源码
string Text = string.Empty;
while (TextInPdu.Length % 14 != 0) //最后一组不满个成员时补"0"
TextInPdu += "0";
char[] a = TextInPdu.ToCharArray(); //将源码存入字符数组a[]
string b = string.Empty;
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;
}
//=====================================================================================
/// <summary>
/// 16进制转成对应二进制字符串 如“1” 转成“0001”
/// </summary>
/// <param name="Hex"></param>
/// <returns></returns>
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();
}
//=====================================================================================
//=====================================================================================
private string GetTextFromPdu_UCS2(string pdu) //解析短信正文函数_UCS2编码
{
//截取PDU串中短信正文部分源码,读者也可用BitConverter函数实现部分转换
string TextInPdu = pdu.Substring(58);
string Text = string.Empty;
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 进制值
}
//=====================================================================================
private string GetTeleFromPdu(string pdu) //解析TeleNumber 函数
{
//截取PDU串中短信发送方电话号码源码
string TeleInPdu = pdu.Substring(26, 12);//被叫号码
string Tele = string.Empty;
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, 14);
string Time = string.Empty;
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; //返回短信日期时间[年-月-日时:分:秒]
}
public static bool RegexValidate(string regexString, string validateString)
{
Regex regex = new Regex(regexString);
return regex.IsMatch(validateString.Trim());
}
#endregion
private void btnSendMsg_Click(object sender, EventArgs e)
{
SendMsg(this.tbPhone.Text, this.tbMsg.Text);
}
/// <summary>
/// 取得字节长度,一个汉字等于2字节
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static int GetLength(string str)
{
if (str.Length == 0)
return 0;
ASCIIEncoding ascii = new ASCIIEncoding();
int tempLen = 0;
byte[] s = ascii.GetBytes(str);
for (int i = 0; i < s.Length; i++)
{
if ((int)s[i] == 63)
tempLen += 2;
else
tempLen += 1;
}
return tempLen;
}
}
}