使用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();

                //如果当前串口数目 > 正在使用的COM
                if (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,如需转载请自行联系原作者
相关文章
|
1月前
|
C#
24. C# 编程:用户设定敌人初始血值的实现
24. C# 编程:用户设定敌人初始血值的实现
18 0
|
2月前
|
SQL 数据库连接 应用服务中间件
C#WinForm基础编程(三)
C#WinForm基础编程
71 0
|
2月前
C#WinForm基础编程(二)
C#WinForm基础编程
55 0
|
2月前
|
C# 数据安全/隐私保护
C#WinForm基础编程(一)
C#WinForm基础编程
59 0
|
4月前
|
数据采集 前端开发 C#
C#编程艺术:Fizzler库助您高效爬取www.twitter.com音频
Twitter是全球最大的社交媒体平台之一,包含丰富的音频资源。用户可以在Twitter上发布、转发、评论和收听各种音频内容,如音乐、播客、新闻、故事等,直接从Twitter抓取音频数据并非易事,尤其是在考虑到可能的封锁和反爬虫机制。Twitter会对频繁访问的IP地址进行限制或封禁,以防止恶意爬虫的行为。因此,我们需要使用一些技术手段来规避这些障碍,确保稳定而高效的数据访问。
C#编程艺术:Fizzler库助您高效爬取www.twitter.com音频
|
3月前
|
程序员 C#
深入理解 C# 编程:枚举、文件处理、异常处理和数字相加
枚举是一个特殊的“类”,表示一组常量(不可更改/只读变量)。 要创建枚举,请使用 enum 关键字(而不是 class 或 interface),并用逗号分隔枚举项:
37 0
|
3月前
|
定位技术 C# 图形学
Unity和C#游戏编程入门:创建迷宫小球游戏示例
Unity和C#游戏编程入门:创建迷宫小球游戏示例
71 2
|
4月前
|
C# C++
C# 高效率编程 “多线程” 的基本使用
C# 高效率编程 “多线程” 的基本使用
|
6月前
|
开发框架 .NET 数据库
asp.net企业费用报销管理信息系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
asp.net 企业费用报销管理信息系统是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为sqlserver2008,使 用c#语言开发 应用技术:asp.net c#+sqlserver 开发工具:vs2010 +sqlserver
49 0