使用C# 实现串口拨号器的SIM卡通信[修正版]
在第一版 使用C# 实现串口拨号器的SIM卡通信 的代码中,存在一些实际的问题,经过反复测试和实际使用,对原代码进行了一些改进。
首先,博客园的ㄟ荖樹炪厊ㄖ同学提出将拨号指令ATD发出后,不必使用 Thread.Sleep(20 * 1000) 方法等待20秒后进行挂机,而改用AutoResetEvent来处理,不必让线程死等,也能提高你程序的性能。但修改后效果并不理想,还是使用Thread.Sleep(20 * 1000) 方法快捷实用。
其次,由于拨号器以及服务器等硬件设备的差异,导致反馈信息的速度不一致,以前采用Thread.Sleep() 方法绑定固定秒数然后查看返回信息的方式存在一定的问题,改用如下代码就行:
int i = 0; while (read.Trim() == "" && i <15) {Thread.Sleep(100);i++; }if (read.Contains("ATH") && read.Contains("OK")) { ...... }
其余的不详细介绍,看代码即可。
MySerialPort 类
对每一个连接到COM 串行端口的拨号器实例化 MySerialPort 对象,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.Ports; using System.Threading;namespace Con_SerialPortExample {public class MySerialPort{private SerialPort com;public MySerialPort(string _portName){this.com = new SerialPort();//this.autoResetEvent_HasModem = new AutoResetEvent(true);//this.autoResetEvent_Dialing = new AutoResetEvent(false);//接收数据事件this.com.DataReceived += new SerialDataReceivedEventHandler(com_DataReceived);//串口名com.PortName = _portName;this.PortName = _portName;// BaudRate 串行波特率com.BaudRate = 9600; //默认值// 是否启用请求发送 (RTS) 信号。com.RtsEnable = true; //由计算机发送 Request To Send 信号到联接的调制解调器,以请示允许发送数据。// 是否使Data Terminal Ready (DTR)线有效。 xugang 2012.8.20 添加//com.DtrEnable = true; //Data Terminal Ready 是计算机发送到调制解调器的信号,指示计算机在等待接受传输。 try{com.Open();}catch //(Exception){Close();}}public MySerialPort(string _portName, int _baudRate): this(_portName){if (_baudRate != 0){// BaudRate 串行波特率com.BaudRate = _baudRate;}}private string portName;//串口名称public string PortName{get { return portName; }set { portName = value; }}// BaudRate 串行波特率public int BaudRate{get { return com.BaudRate; }set { com.BaudRate = value; }}private bool isWorking;//设置是否正在使用public bool IsWorking{get { return isWorking; }set { isWorking = value; }}//private enum AutoResetEvent_Type//{// None, HasModem, Dialing//}//private AutoResetEvent autoResetEvent_HasModem;//private AutoResetEvent autoResetEvent_Dialing;//private AutoResetEvent_Type autoResetEvent_Type = AutoResetEvent_Type.None;//private bool enableUse;private DateTime lastUseTime = DateTime.MinValue;//当前拨号器能否使用 (解决连续使用拨号问题)public bool EnableUse{get {TimeSpan span = DateTime.Now - lastUseTime;return span.TotalSeconds >= 60; }//set { enableUse = value; }}// 检测当前端口是否安装有拨号器public bool HasModem(){//this.autoResetEvent_Type = AutoResetEvent_Type.HasModem;//read = ""; //清空返回缓冲区WriteSerial("AT\r\n");//Thread.Sleep(500);//this.autoResetEvent_HasModem.WaitOne(5 * 1000);//this.autoResetEvent_Type = AutoResetEvent_Type.None;int i = 0;while (read.Trim() == "" && i < 40){Thread.Sleep(100);i++;}Console.WriteLine(read.TrimEnd('\r', '\n'));//Console.Write(read);//if (read.Contains("AT") && read.Contains("OK"))if (read.Contains("OK")){Console.WriteLine(this.com.PortName + "端口能使用!");return true;}else{Console.WriteLine(this.com.PortName + "端口无法使用!");return false;}}//进行拨号,唤醒上位机public void Dialing(string _SIM){IsWorking = true; //正在拨号try{//this.autoResetEvent_Type = AutoResetEvent_Type.Dialing;read = ""; //清空返回缓冲区StringBuilder sb = new StringBuilder(36);sb.Append(this.com.PortName);sb.Append("端口开始对SIM卡:");sb.Append(_SIM);sb.Append(" 进行拨号...");Console.WriteLine(sb.ToString());WriteSerial(string.Format("ATD{0};\r\n", _SIM));System.Threading.Thread.Sleep(20 * 1000);//this.autoResetEvent_Dialing.WaitOne(20 * 1000);//this.autoResetEvent_Type = AutoResetEvent_Type.None;read = ""; //清空返回缓冲区WriteSerial("ATH\r\n");//this.autoResetEvent.WaitOne(1000);int i = 0;while (read.Trim() == "" && i <15){Thread.Sleep(100);i++;}Console.WriteLine(read);if (read.Contains("ATH") && read.Contains("OK")){Console.WriteLine(this.com.PortName + "端口拨号已完成!");}else{read = ""; //清空返回缓冲区//System.Threading.Thread.Sleep(1000);WriteSerial("ATH\r\n");//this.autoResetEvent.WaitOne(1000);i = 0;while (read.Trim() == "" && i < 20){Thread.Sleep(100);i++;}Console.WriteLine(read);if (read.Contains("ATH") && read.Contains("OK")){Console.WriteLine(this.com.PortName + "端口拨号已完成!");}else{//IsWorking = false; //拨号完成string strExc = this.com.PortName + "拨号异常!";Console.WriteLine(strExc);throw new Exception(strExc);}}}catch (Exception ex){throw ex;}finally{IsWorking = false; //拨号完成}}/// <summary>/// 向串口端发送命令!/// </summary>/// <param name="s">命令字符串</param>private void WriteSerial(string s){//this.autoResetEvent_HasModem.WaitOne(20*1000);//mLogger.Info(s);byte[] buff = Encoding.ASCII.GetBytes(s);try{this.com.Write(buff, 0, buff.Length);}catch (Exception ex){//WriteExecLog.Writing(ex);Console.WriteLine(ex.Message);}}//int n = 0;string read = "";//接收数据事件方法void com_DataReceived(object sender, SerialDataReceivedEventArgs e){if (sender is SerialPort){try{Thread.Sleep(500);read = ""; //清空返回缓冲区SerialPort mySerial = sender as SerialPort;if (mySerial.IsOpen == false){mySerial.Open();}//方法一//read += mySerial.ReadLine().Trim();//Console.WriteLine(mySerial.PortName + " 第" + n.ToString() + "接收数据:" + read);//方法二byte[] readBuffer = new byte[1024];int count = mySerial.Read(readBuffer, 0, 1023);if (count > 0){read = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count);//Console.WriteLine(read);}//AT 返回//if (autoResetEvent_Type == AutoResetEvent_Type.HasModem// && read.Contains("OK"))//{// this.autoResetEvent_HasModem.Set();//}//ATD 返回//if (autoResetEvent_Type == AutoResetEvent_Type.Dialing// && (read.Contains("ERROR") || read.Contains("BUSY") || read.Contains("OK") || read.Contains("NO CARRIER"))// )//{// this.autoResetEvent_Dialing.Set();//}}catch (TimeoutException){return; //xg备忘:可以将异常写入日志!}catch (Exception){return; //xg备忘:可以将异常写入日志!}//finally//{// this.autoResetEvent_HasModem.Set();//}}}//private string ReadSerial()//{// while (_keepReading)// {// if (com.IsOpen)// {// //byte[] readBuffer = new byte[com.ReadBufferSize + 1];// byte[] readBuffer = new byte[10];// try// {// //int count = com.Read(readBuffer, 0, com.ReadBufferSize);// int count = com.Read(readBuffer, 0, 9);// String SerialIn = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count);// if (count != 0)// {// return SerialIn;// }// }// catch (TimeoutException) // {// return "";// }// }// else// {// TimeSpan waitTime = new TimeSpan(0, 0, 0, 0, 50);// Thread.Sleep(waitTime);// }// }// return "";//}//关闭public void Close(){if (com != null){com.Close();com.Dispose();}}}}
SerialPortList 类
定义一个 SerialPortList 类,实现对所有连接上的拨号器 MySerialPort 对象进行管理和调度使用。代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.Ports; using System.Threading;namespace Con_SerialPortExample {public class SerialPortList{//已经安装了拨号器的串口对象private List<MySerialPort> al = null;//可使用的拨号器个数public int WorkableCount{get{if (al == null) return 0;else return al.Count;}}private Dictionary<string, int> portBaudRate = null;//波特率配置列表public Dictionary<string, int> PortBaudRate{get { return portBaudRate; }set { portBaudRate = value; }}private bool hasPort = false;//当前有无可使用的串口拨号器public bool HasPort{get { return hasPort; }//set { hasPort = value; }}private int reCheckMinutes = 30; //默认30分钟//串口拨号器的重新检测间隔分钟public int ReCheckMinutes{get { return reCheckMinutes; }set { reCheckMinutes = value; }}#region 构造方法重载public SerialPortList() { }public SerialPortList(Dictionary<string, int> _portBaudRate){this.portBaudRate = _portBaudRate;}public SerialPortList(int _reCheckMinutes){this.reCheckMinutes = _reCheckMinutes;}public SerialPortList(Dictionary<string, int> _portBaudRate, int _reCheckMinutes){this.portBaudRate = _portBaudRate;this.reCheckMinutes = _reCheckMinutes;}#endregion 构造方法重载/// <summary>/// 获得串口上已经安装了拨号器的对象/// </summary>public void GetSerialPortList(){al = new List<MySerialPort>();//步骤一: 获得所有的串口名称(列表)string[] ports = SerialPort.GetPortNames();foreach (string port in ports){MySerialPort mySerialPort = null;Console.WriteLine("正在检测:" + port); //测试使用//是否设置波特率?if (portBaudRate != null&& portBaudRate.ContainsKey(port)&& portBaudRate[port] != 0){mySerialPort = new MySerialPort(port, portBaudRate[port]);}else mySerialPort = new MySerialPort(port);bool ok = mySerialPort.HasModem();if (ok){al.Add(mySerialPort);}else{mySerialPort.Close();mySerialPort = null;}}// 判断当前计算机有无可使用串口端hasPort = al.Count <= 0 ? false : true;}/// <summary>/// 重新获得串口上已经安装了拨号器的对象/// </summary>public void ReGetSerialPortList(){if (al == null) GetSerialPortList();else{//步骤一: 重新获得所有的串口名称(列表)string[] portsName_2 = SerialPort.GetPortNames();//如果当前串口数目 > 正在使用的COMif (portsName_2.Length > al.Count){Console.WriteLine("正在重新检测可以使用的拨号器!"); //测试使用foreach (string pName_2 in portsName_2){//当前串口名是否存在拨号列表中bool hasAt = al.Exists(delegate(MySerialPort port_1){return pName_2 == port_1.PortName;});//如果当前串口名不存在拨号列表中,则重新检测!if (!hasAt){Console.WriteLine("正在重新检测:" + pName_2); //测试使用MySerialPort mySerialPort = null;//是否设置波特率?if (portBaudRate != null&& portBaudRate.ContainsKey(pName_2)&& portBaudRate[pName_2] != 0){mySerialPort = new MySerialPort(pName_2, portBaudRate[pName_2]);}else mySerialPort = new MySerialPort(pName_2);bool ok = mySerialPort.HasModem();if (ok){al.Add(mySerialPort);}else{mySerialPort.Close();mySerialPort = null;}}}}}// 判断当前计算机有无可使用串口端hasPort = al.Count <= 0 ? false : true;}/// <summary>/// 重新获得串口上已经安装了拨号器的对象 (波特率使用默认值)/// </summary>public void ReGetSerialPortList(int _reCheckMinutes){//串口拨号器的重新检测间隔分钟reCheckMinutes = _reCheckMinutes;ReGetSerialPortList();//波特率全部使用默认值}/// <summary>/// 释放所有串口资源组件/// </summary>public void ClearAllSerialPort(){if (al != null){for (int i = 0; i < al.Count; i++){al[i].Close();al[i] = null;}al = null;}if (portBaudRate != null){portBaudRate = null;}}private int index_Number = -1;//串口的调度号private int IndexNumber(){lock (this){if (index_Number + 1 >= al.Count){if (al.Count == 0) index_Number = -1;else index_Number = 0;}else{index_Number++;}return index_Number;}}/// <summary>/// 对已经安装了拨号器的串口调度使用/// </summary>private void UseingSerialPort(string _SIM){if (al == null) return;// 等待线程进入 Monitor.Enter(al);MySerialPort getPort = null;try{//获得当前调用的串口对象的索引号int num = IndexNumber();if (num >= 0) //判断是否存在拨号器{getPort = al[num];if (getPort != null && !getPort.IsWorking){getPort.Dialing(_SIM); //对 SIM 进行拨号,唤醒上位机}}else{//没有可使用的拨号器,则重新检测。this.ReGetSerialPortList();}}catch{//再一次检查该 COM 能否使用! (范工提议)if (getPort != null){string re_PortName = getPort.PortName;al.Remove(getPort); //从可用列表去除getPort.Close();MySerialPort mySerialPort = new MySerialPort(re_PortName);bool ok = mySerialPort.HasModem();if (ok){al.Add(mySerialPort); //重新添加到列表}else{mySerialPort.Close();mySerialPort = null;}}}finally{// 通知其它对象Monitor.Pulse(al);// 释放对象锁 Monitor.Exit(al);}}//重新检测端口时间private DateTime dtCheck = DateTime.Now;/// <summary>/// 调用拨号器/// </summary>/// <param name="_SIM"></param>public void InvokingSerialPort(string _SIM){if (hasPort == false){// 获得串口上已经安装了拨号器的对象this.GetSerialPortList();}if (hasPort == true){this.UseingSerialPort(_SIM);//Thread.Sleep(5000);}//定期检测串口列表//if (dtCheck.AddMinutes(reCheckMinutes) <= DateTime.Now)if (dtCheck.AddMinutes(5) <= DateTime.Now){// 重新获得串口上已经安装了拨号器的对象this.ReGetSerialPortList();dtCheck = DateTime.Now;}}}}
测试代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; // //using System.IO.Ports; using System.Threading; //using System.Collections;namespace Con_SerialPortExample {class Program{static void Main(string[] args){// 获得串口上已经安装了拨号器的对象 (自定义波特率)Dictionary<string, int> _portBaudRate = new Dictionary<string, int>();_portBaudRate["COM1"] = 9600;_portBaudRate["COM2"] = 9600;_portBaudRate["COM7"] = 9600;SerialPortList list = new SerialPortList(_portBaudRate,5);try{// 获得串口上已经安装了拨号器的对象list.GetSerialPortList();if (list.HasPort == false){Console.WriteLine("当前计算机无可使用的串口拨号器!");}while (list.HasPort){// 调用拨号器list.InvokingSerialPort("13500000000"); Thread.Sleep(5000);}Console.WriteLine("程序运行结束!");}finally{// 释放所有串口资源组件list.ClearAllSerialPort();}Console.ReadLine();}} }本文转自钢钢博客园博客,原文链接http://www.cnblogs.com/xugang/archive/2012/09/19/2693856.html,如需转载请自行联系原作者
使用C# 实现串口拨号器的SIM卡通信[修正版]相关推荐
- 使用C# 实现串口拨号器的SIM卡通信
写此博客意为抛砖引玉,希望能和博客园的朋友们探讨一下关于.NET 在工业方面的应用,包括:物联网.无线通信.嵌入式开发.工业控制等等.欢迎探讨,多多指教!^_^ 下面是我在开发中,使用C#代码实现对安 ...
- 使用SerialPort 对象实现串口拨号器通信[下]
定义 ModemManager 调度管理类 ModemManager 类用于对所有 Modem 对象进行管理和调度使用.ModemManager 类代码如下: using System; using ...
- Android入门——电话拨号器和4种点击事件
关于HelloWorld为,电话拨号程序还AndroidA入门demo,从这个样例我们要理清楚做安卓项目的思路. 大体分为三步: 1.理解需求,理清思路 2.设计UI 3.代码实现 电话拨号器 1. ...
- Android 拨号器
2019独角兽企业重金招聘Python工程师标准>>> 一.实现思路: 1.实现界面,界面上有EditText 控件, Button 控件 2.资源文件 values 下创建str ...
- 实例教程一:电话拨号器
仿愤怒的小鸟小游戏 http://www.eoeandroid.com/thread-205532-1-1.html Android 闯关游戏源码 http://www.eoeandroid.com/ ...
- android案例一 电话拨号器
效果图: 电话拨号器的核心原理: 意图 MainActivity代码: private EditTest et_number; //加载一个布局 setContentView(R.layout.Ma ...
- Android-简单拨号器案例
Android [19]简单电话拨号器 @方法步骤 1.新建一个android程序,项目名设置为 phone ,然后打开 phone->res->layout->activity ...
- 简单拨号器(Android)
感受: 1.了解了intent中的action和Uri. 2.了解了向下一个活动传递数据. 3.了解了内容提供器. 4.了解自定义适配器. 4.其实T9拨号器和简单计算器原理一样. 代码: MainA ...
- Android入门——电话拨号器和四种点击事件
相对于HelloWorld来说,电话拨号器也是Android的一个入门demo,从这个样例我们要理清楚做安卓项目的思路. 大体分为三步: 1.理解需求,理清思路 2.设计UI 3.代码实现 电话拨号器 ...
最新文章
- python保存list
- FIFOQueue '_4_batch_processing/batch_join/fifo_queue' is closed and has insu
- rs485调试软件_5种RS485切换方向的方法及优劣势分析
- ML之xgboost:利用xgboost算法(sklearn+3Split+调参曲线+EarlyStop)训练mushroom蘑菇数据集(22+1,6513+1611)来预测蘑菇是否毒性(二分类预测)
- k8s 离线安装_阿里开源 k8s 事件通知服务
- springMVC接收前端参数的方式
- SQL Server 2008 R2的发布订阅配置实践
- 在线安装TIDB集群
- ASP.NET MVC 实现二级域名(泛域名)
- [LeetCode] 3. Longest Substring Without Repeating Characters 题解
- 基础算法 —— 调度问题
- 截取台风后的图片_Python数据分析案例 | 台风最喜欢在我国哪个省市登陆
- 快速入门上手第一课 | 从云计算到 Serverless
- 在Linux系统中允许或拒绝SSH访问特定用户或组的方法
- 华为手机asph啥机型_2020年最值得买大品牌手机盘点:华为vivo等五款机型入围
- 主析取范式与主合取范式
- Boost.Asio Library
- 金融人都在选择的硕士项目---中国人民大学与加拿大女王大学金融硕士
- 关于五险一金(南京)
- 7-3 垃圾分类 (20分)
热门文章
- 2010/12/19英语单词背诵
- 申宝证券-个股分化指数窄幅整理
- android架构师解压密码,咕泡Java架构师第三期完整版
- 使用linux配置环境变量后发现命令不能用问题
- 方图智能:精准洞察市场需求 打造细分市场新标杆
- Why Python is Slow? Looking Under the Hood
- 如何构建全球实时音视频云及其海外网络传输优化
- python一行代码实现动态爱心
- 在线程中调用PJSIP中的呼叫出现提示注册线程pj_thread_register的解决方法
- Vue中使用js-web-screen-shot插件实现截屏功能