在第一版 使用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卡通信[修正版]相关推荐

  1. 使用C# 实现串口拨号器的SIM卡通信

    写此博客意为抛砖引玉,希望能和博客园的朋友们探讨一下关于.NET 在工业方面的应用,包括:物联网.无线通信.嵌入式开发.工业控制等等.欢迎探讨,多多指教!^_^ 下面是我在开发中,使用C#代码实现对安 ...

  2. 使用SerialPort 对象实现串口拨号器通信[下]

    定义 ModemManager 调度管理类 ModemManager 类用于对所有 Modem 对象进行管理和调度使用.ModemManager 类代码如下: using System; using ...

  3. Android入门——电话拨号器和4种点击事件

    关于HelloWorld为,电话拨号程序还AndroidA入门demo,从这个样例我们要理清楚做安卓项目的思路. 大体分为三步: 1.理解需求,理清思路 2.设计UI 3.代码实现 电话拨号器 1.  ...

  4. Android 拨号器

    2019独角兽企业重金招聘Python工程师标准>>> 一.实现思路: 1.实现界面,界面上有EditText  控件, Button 控件 2.资源文件 values 下创建str ...

  5. 实例教程一:电话拨号器

    仿愤怒的小鸟小游戏 http://www.eoeandroid.com/thread-205532-1-1.html Android 闯关游戏源码 http://www.eoeandroid.com/ ...

  6. android案例一 电话拨号器

    效果图: 电话拨号器的核心原理:  意图 MainActivity代码: private EditTest et_number; //加载一个布局 setContentView(R.layout.Ma ...

  7. Android-简单拨号器案例

    Android [19]简单电话拨号器 @方法步骤 1.新建一个android程序,项目名设置为 phone  ,然后打开  phone->res->layout->activity ...

  8. 简单拨号器(Android)

    感受: 1.了解了intent中的action和Uri. 2.了解了向下一个活动传递数据. 3.了解了内容提供器. 4.了解自定义适配器. 4.其实T9拨号器和简单计算器原理一样. 代码: MainA ...

  9. Android入门——电话拨号器和四种点击事件

    相对于HelloWorld来说,电话拨号器也是Android的一个入门demo,从这个样例我们要理清楚做安卓项目的思路. 大体分为三步: 1.理解需求,理清思路 2.设计UI 3.代码实现 电话拨号器 ...

最新文章

  1. python保存list
  2. FIFOQueue '_4_batch_processing/batch_join/fifo_queue' is closed and has insu
  3. rs485调试软件_5种RS485切换方向的方法及优劣势分析
  4. ML之xgboost:利用xgboost算法(sklearn+3Split+调参曲线+EarlyStop)训练mushroom蘑菇数据集(22+1,6513+1611)来预测蘑菇是否毒性(二分类预测)
  5. k8s 离线安装_阿里开源 k8s 事件通知服务
  6. springMVC接收前端参数的方式
  7. SQL Server 2008 R2的发布订阅配置实践
  8. 在线安装TIDB集群
  9. ASP.NET MVC 实现二级域名(泛域名)
  10. [LeetCode] 3. Longest Substring Without Repeating Characters 题解
  11. 基础算法 —— 调度问题
  12. 截取台风后的图片_Python数据分析案例 | 台风最喜欢在我国哪个省市登陆
  13. 快速入门上手第一课 | 从云计算到 Serverless
  14. 在Linux系统中允许或拒绝SSH访问特定用户或组的方法
  15. 华为手机asph啥机型_2020年最值得买大品牌手机盘点:华为vivo等五款机型入围
  16. 主析取范式与主合取范式
  17. Boost.Asio Library
  18. 金融人都在选择的硕士项目---中国人民大学与加拿大女王大学金融硕士
  19. 关于五险一金(南京)
  20. 7-3 垃圾分类 (20分)

热门文章

  1. 2010/12/19英语单词背诵
  2. 申宝证券-个股分化指数窄幅整理
  3. android架构师解压密码,咕泡Java架构师第三期完整版
  4. 使用linux配置环境变量后发现命令不能用问题
  5. 方图智能:精准洞察市场需求 打造细分市场新标杆
  6. Why Python is Slow? Looking Under the Hood
  7. 如何构建全球实时音视频云及其海外网络传输优化
  8. python一行代码实现动态爱心
  9. 在线程中调用PJSIP中的呼叫出现提示注册线程pj_thread_register的解决方法
  10. Vue中使用js-web-screen-shot插件实现截屏功能