对比SerialCommunication和微软的SerialPort,向SerialPort看齐
SerialCommunication是我综合网上看到的代码稍作修改而成的串口通信类,而SerialPort则是C#的System类库的IO目录Ports子目录下的串口通信类。SerialCommunication只有区区的二百多行,而SerialPort则有几千行。下面我将介绍SerialPort相对于SerialCommunication好在哪里。
首先是一些基本字段的对比。
/// <summary>
/// 波特率
/// </summary>
public int BaudRate;
/// <summary>
/// 数据位
/// </summary>
public byte ByteSize = 8;
/// <summary>
/// 奇偶校验,0-4=no,odd,even,mark,space
/// </summary>
public byte Parity = 1;
/// <summary>
/// 串口号
/// </summary>
public int PortNum;
/// <summary>
/// 读超时
/// </summary>
public UInt32 ReadTimeout = 1000;
/// <summary>
/// 停止位,0,1,2 = 1, 1.5, 2
/// </summary>
public byte StopBits;
public SerialPort()
{
this.baudRate = 0x2580;
this.dataBits = 8;
this.stopBits = System.IO.Ports.StopBits.One;
this.portName = "COM1";
this.encoding = System.Text.Encoding.ASCII;
this.decoder = System.Text.Encoding.ASCII.GetDecoder();
this.maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1);
this.readTimeout = -1;
this.writeTimeout = -1;
this.receivedBytesThreshold = 1;
this.parityReplace = 0x3f;
this.newLine = "\n";
this.readBufferSize = 0x1000;
this.writeBufferSize = 0x800;
this.inBuffer = new byte[0x400];
this.oneChar = new char[1];
}
public SerialPort(IContainer container)
{
this.baudRate = 0x2580;
this.dataBits = 8;
this.stopBits = System.IO.Ports.StopBits.One;
this.portName = "COM1";
this.encoding = System.Text.Encoding.ASCII;
this.decoder = System.Text.Encoding.ASCII.GetDecoder();
this.maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1);
this.readTimeout = -1;
this.writeTimeout = -1;
this.receivedBytesThreshold = 1;
this.parityReplace = 0x3f;
this.newLine = "\n";
this.readBufferSize = 0x1000;
this.writeBufferSize = 0x800;
this.inBuffer = new byte[0x400];
this.oneChar = new char[1];
container.Add(this);
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public SerialPort(string portName) : this(portName, 0x2580, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One)
{
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public SerialPort(string portName, int baudRate) : this(portName, baudRate, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One)
{
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public SerialPort(string portName, int baudRate, System.IO.Ports.Parity parity) : this(portName, baudRate, parity, 8, System.IO.Ports.StopBits.One)
{
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public SerialPort(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits) : this(portName, baudRate, parity, dataBits, System.IO.Ports.StopBits.One)
{
}
public SerialPort(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits, System.IO.Ports.StopBits stopBits)
{
this.baudRate = 0x2580;
this.dataBits = 8;
this.stopBits = System.IO.Ports.StopBits.One;
this.portName = "COM1";
this.encoding = System.Text.Encoding.ASCII;
this.decoder = System.Text.Encoding.ASCII.GetDecoder();
this.maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1);
this.readTimeout = -1;
this.writeTimeout = -1;
this.receivedBytesThreshold = 1;
this.parityReplace = 0x3f;
this.newLine = "\n";
this.readBufferSize = 0x1000;
this.writeBufferSize = 0x800;
this.inBuffer = new byte[0x400];
this.oneChar = new char[1];
this.PortName = portName;
this.BaudRate = baudRate;
this.Parity = parity;
this.DataBits = dataBits;
this.StopBits = stopBits;
}
可以看到我有一个明显的不足,就是用了public的字段,这貌似是违反了面向对象的理念的,按照面向对象的理念,外部类应该是不能直接访问操作其他类的内部字段的,不过有些人就认为写private字段,public属性太麻烦,过度设计,能不用属性就不用属性。这里就不讨论用属性的优点了。除此之外还可以看到SerialPort有个明显的优点,使用了大量的重载方法,那些重载方法之间又用了继承,最终执行了参数最多的那个重载方法,该方法里面又将那些参数赋值给了对应的属性,各属性赋值时候又可以进行各种判断,这就是C#的经典套路,也是比较好的套路,只是这样写代码会长很多。
然后看串口打开方法
public void Open()
{
// OPEN THE COMM PORT.
_hComm = CreateFile("COM" + PortNum, GenericRead | GenericWrite, 0, 0, OpenExisting, 0, 0);
// IF THE PORT CANNOT BE OPENED, BAIL OUT.
if (_hComm == InvalidHandleValue)
{
throw (new Exception("Port Open Failure"));
}
CommTimeouts ctoCommPort = new CommTimeouts { ReadTotalTimeoutConstant = ReadTimeout };
if (!SetCommTimeouts(_hComm, ref ctoCommPort))
{
throw (new Exception("Bad Timeout Settings"));
}
Dcb dcb = new Dcb();
GetCommState(_hComm, ref dcb);
dcb.BaudRate = BaudRate;
dcb.Parity = Parity;
dcb.ByteSize = ByteSize;
dcb.StopBits = StopBits;
if (!SetCommState(_hComm, ref dcb))
{
throw (new Exception("Bad Com Settings"));
}
}
public void Open()
{
if (this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_already_open"));
}
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
this.internalSerialStream = new SerialStream(this.portName, this.baudRate, this.parity, this.dataBits, this.stopBits, this.readTimeout, this.writeTimeout, this.handshake, this.dtrEnable, this.rtsEnable, this.discardNull, this.parityReplace);
this.internalSerialStream.SetBufferSizes(this.readBufferSize, this.writeBufferSize);
this.internalSerialStream.ErrorReceived += new SerialErrorReceivedEventHandler(this.CatchErrorEvents);
this.internalSerialStream.PinChanged += new SerialPinChangedEventHandler(this.CatchPinChangedEvents);
this.internalSerialStream.DataReceived += new SerialDataReceivedEventHandler(this.CatchReceivedEvents);
}
internal SerialStream(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits, System.IO.Ports.StopBits stopBits, int readTimeout, int writeTimeout, System.IO.Ports.Handshake handshake, bool dtrEnable, bool rtsEnable, bool discardNull, byte parityReplace)
{
int dwFlagsAndAttributes = 0x40000000;
if (Environment.OSVersion.Platform == PlatformID.Win32Windows)
{
dwFlagsAndAttributes = 0x80;
this.isAsync = false;
}
if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException(SR.GetString("Arg_InvalidSerialPort"), "portName");
}
SafeFileHandle hFile = Microsoft.Win32.UnsafeNativeMethods.CreateFile(@"\\.\" + portName, -1073741824, 0, IntPtr.Zero, 3, dwFlagsAndAttributes, IntPtr.Zero);
if (hFile.IsInvalid)
{
InternalResources.WinIOError(portName);
}
try
{
int fileType = Microsoft.Win32.UnsafeNativeMethods.GetFileType(hFile);
if ((fileType != 2) && (fileType != 0))
{
throw new ArgumentException(SR.GetString("Arg_InvalidSerialPort"), "portName");
}
this._handle = hFile;
this.portName = portName;
this.handshake = handshake;
this.parityReplace = parityReplace;
this.tempBuf = new byte[1];
this.commProp = new Microsoft.Win32.UnsafeNativeMethods.COMMPROP();
int lpModemStat = 0;
if (!Microsoft.Win32.UnsafeNativeMethods.GetCommProperties(this._handle, ref this.commProp) || !Microsoft.Win32.UnsafeNativeMethods.GetCommModemStatus(this._handle, ref lpModemStat))
{
int errorCode = Marshal.GetLastWin32Error();
switch (errorCode)
{
case 0x57:
case 6:
throw new ArgumentException(SR.GetString("Arg_InvalidSerialPortExtended"), "portName");
}
InternalResources.WinIOError(errorCode, string.Empty);
}
if ((this.commProp.dwMaxBaud != 0) && (baudRate > this.commProp.dwMaxBaud))
{
throw new ArgumentOutOfRangeException("baudRate", SR.GetString("Max_Baud", new object[] { this.commProp.dwMaxBaud }));
}
this.comStat = new Microsoft.Win32.UnsafeNativeMethods.COMSTAT();
this.dcb = new Microsoft.Win32.UnsafeNativeMethods.DCB();
this.InitializeDCB(baudRate, parity, dataBits, stopBits, discardNull);
this.DtrEnable = dtrEnable;
this.rtsEnable = this.GetDcbFlag(12) == 1;
if ((handshake != System.IO.Ports.Handshake.RequestToSend) && (handshake != System.IO.Ports.Handshake.RequestToSendXOnXOff))
{
this.RtsEnable = rtsEnable;
}
if (readTimeout == 0)
{
this.commTimeouts.ReadTotalTimeoutConstant = 0;
this.commTimeouts.ReadTotalTimeoutMultiplier = 0;
this.commTimeouts.ReadIntervalTimeout = -1;
}
else if (readTimeout == -1)
{
this.commTimeouts.ReadTotalTimeoutConstant = -2;
this.commTimeouts.ReadTotalTimeoutMultiplier = -1;
this.commTimeouts.ReadIntervalTimeout = -1;
}
else
{
this.commTimeouts.ReadTotalTimeoutConstant = readTimeout;
this.commTimeouts.ReadTotalTimeoutMultiplier = -1;
this.commTimeouts.ReadIntervalTimeout = -1;
}
this.commTimeouts.WriteTotalTimeoutMultiplier = 0;
this.commTimeouts.WriteTotalTimeoutConstant = (writeTimeout == -1) ? 0 : writeTimeout;
if (!Microsoft.Win32.UnsafeNativeMethods.SetCommTimeouts(this._handle, ref this.commTimeouts))
{
InternalResources.WinIOError();
}
if (this.isAsync && !ThreadPool.BindHandle(this._handle))
{
throw new IOException(SR.GetString("IO_BindHandleFailed"));
}
Microsoft.Win32.UnsafeNativeMethods.SetCommMask(this._handle, 0x1fb);
this.eventRunner = new EventLoopRunner(this);
new Thread(new ThreadStart(this.eventRunner.WaitForCommEvent)) { IsBackground = true }.Start();
}
catch
{
hFile.Close();
this._handle = null;
throw;
}
}
打开方法SerialPort比我的复杂很多,我那个只是打开并设置了一下串口,SerialPort除此之外还开启了ErrorReceived、PinChanged和DataReceived事件。两个接收事件和一个貌似是串口端口改变事件吧。前两个不写是因为我觉得有些人串口通信只需要写不需要接收,那么那两个事件也就不需要了吧,如果需要接收的话调用我那个Read方法就可以接收消息了,不过需要调用者自己开线程监听。
不说这个,就说代码方面的问题吧,我那里有个明显的不太好的地方就是,抛出的异常没有进行分类,异常是有格式的,也可以自定义异常,我那里没有对异常进行分类,全都是抛出Exception就完了,这样的话调用者就不能方便的根据异常的类型进行不同的处理了。除此之外抛出的异常也不够详细,SerialPort的Open方法一开始就有一个Port_already_open端口已打开异常,我那边就没有分的这么细了,只要端口打开失败就返回Port Open Failure,异常分的不够细也是不好的。
除此之外就是SerialPort用了大量的枚举,如StopBits、Handshake、Parity、SerialData、SerialError和SerialPinChange,用枚举有个很大的好处就是调用者可以清楚的根据枚举来判断每个枚举值的意义,这样子调用者可以在不查文档的情况下就顺利的写完了代码,这可能会节省编码人员很多的时间,与用枚举损失的些须效率,这是完全值得的,我没有用枚举只是贪图方便省事,这是不够好的,应该注意一下。
接着看串口关闭方法
public void Close()
{
if (_hComm != InvalidHandleValue)
{
CloseHandle(_hComm);
}
}
public void Close()
{
base.Dispose();
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (this)
{
if ((this.site != null) && (this.site.Container != null))
{
this.site.Container.Remove(this);
}
if (this.events != null)
{
EventHandler handler = (EventHandler) this.events[EventDisposed];
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
}
}
关闭方法就完全不同了,我那关闭方法只是把串口关闭,SerialPort则貌似是把整个类Dispose了,这两种做法萝卜青菜各有所爱吧。不过我那个类貌似没有继承自IDisposable貌似不太好,比较大的类应该要提供Dispose才好的吧。
最后看看读写方法吧
public byte[] Read(int numBytes)
{
if (_hComm == InvalidHandleValue)
{
throw (new Exception("Comm Port Not Open"));
}
int bytesRead = 0;
byte[] bufBytes = new byte[numBytes];
ReadFile(_hComm, bufBytes, numBytes, ref bytesRead, ref _ovlCommPort);
byte[] outBytes = new byte[bytesRead];
Array.Copy(bufBytes, outBytes, bytesRead);
return outBytes;
}
public void Write(byte[] writeBytes)
{
if (_hComm == InvalidHandleValue)
{
throw (new Exception("Comm Port Not Open"));
}
WriteFile(_hComm, writeBytes, writeBytes.Length, ref _bytesCount, ref _ovlCommPort);
}
public int Read(byte[] buffer, int offset, int count)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (buffer == null)
{
throw new ArgumentNullException("buffer", SR.GetString("ArgumentNull_Buffer"));
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if ((buffer.Length - offset) < count)
{
throw new ArgumentException(SR.GetString("Argument_InvalidOffLen"));
}
int num = 0;
if (this.CachedBytesToRead >= 1)
{
num = Math.Min(this.CachedBytesToRead, count);
Buffer.BlockCopy(this.inBuffer, this.readPos, buffer, offset, num);
this.readPos += num;
if (num == count)
{
if (this.readPos == this.readLen)
{
this.readPos = this.readLen = 0;
}
return count;
}
if (this.BytesToRead == 0)
{
return num;
}
}
this.readLen = this.readPos = 0;
int num2 = count - num;
num += this.internalSerialStream.Read(buffer, offset + num, num2);
this.decoder.Reset();
return num;
}
public int Read(char[] buffer, int offset, int count)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (buffer == null)
{
throw new ArgumentNullException("buffer", SR.GetString("ArgumentNull_Buffer"));
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if ((buffer.Length - offset) < count)
{
throw new ArgumentException(SR.GetString("Argument_InvalidOffLen"));
}
return this.InternalRead(buffer, offset, count, this.readTimeout, false);
}
public void Write(string text)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (text == null)
{
throw new ArgumentNullException("text");
}
if (text.Length != 0)
{
byte[] bytes = this.encoding.GetBytes(text);
this.internalSerialStream.Write(bytes, 0, bytes.Length, this.writeTimeout);
}
}
public void Write(byte[] buffer, int offset, int count)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (buffer == null)
{
throw new ArgumentNullException("buffer", SR.GetString("ArgumentNull_Buffer"));
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if ((buffer.Length - offset) < count)
{
throw new ArgumentException(SR.GetString("Argument_InvalidOffLen"));
}
if (buffer.Length != 0)
{
this.internalSerialStream.Write(buffer, offset, count, this.writeTimeout);
}
}
public void Write(char[] buffer, int offset, int count)
{
if (!this.IsOpen)
{
throw new InvalidOperationException(SR.GetString("Port_not_open"));
}
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired"));
}
if ((buffer.Length - offset) < count)
{
throw new ArgumentException(SR.GetString("Argument_InvalidOffLen"));
}
if (buffer.Length != 0)
{
byte[] buffer2 = this.Encoding.GetBytes(buffer, offset, count);
this.Write(buffer2, 0, buffer2.Length);
}
}
public void WriteLine(string text)
{
this.Write(text + this.NewLine);
}
对比可以得出我那个读写方法没有SerialPort那么多,我那里只提供了byte[]的读写,这是相对来说比较不够用的,不过这个不是问题。问题是我那里读写时的异常判断太少,只判断了串口关闭的情况,这样有些异常就不能在收发之前被判断出来了。
总结来说就是自己写的代码面向对象得还不够,代码不够规范,考虑的不够全面等,希望以后可以学着向“SerialPort”看齐!
对比SerialCommunication和微软的SerialPort,向SerialPort看齐的更多相关文章
- [转]微软SerialPort秘籍[SerialPort为什么死锁程序的分析]
既然是秘籍,显然是写一些大家不常找到的,MSDN里遗漏提示大家注意的东西. 用过.net 2.0中,自带SerialPort的人,大多都遇到过.莫名其妙的执行Close的时候会死掉的问题.而Wince ...
- SerialPort.h SerialPort.cpp
SerialPort.h 1 #ifndef __SERIALPORT_H__ 2 #define __SERIALPORT_H__ 3 4 #define WM_COMM_BREAK_DETECTE ...
- 使用SerialPort 读取外置GPS信息和使用GeoCoordinateWatcher获取内置gps的信息
简介最近工作中需要读取gps设备的信息,平板本身有内置的gps设备,但是精度不够,就又添加了一个外置的gps.对于外置的gps,我们主要通过SerialPort类来获得串口的信息,然后对接收到的内容进 ...
- SerialPort
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data ...
- [转]How to display the data read in DataReceived event handler of serialport
本文转自:https://stackoverflow.com/questions/11590945/how-to-display-the-data-read-in-datareceived-event ...
- [转]c# System.IO.Ports SerialPort Class
本文转自:https://docs.microsoft.com/en-us/dotnet/api/system.io.ports.serialport?redirectedfrom=MSDN& ...
- C# 串口类SerialPort的使用方法
序言:最近做了一个智能体育项目——跆拳道积分系统,硬件部分会向软件传入振动值等数据,链接方式为串口,所以用到SerialPort类. 值得注意的是: DataReceived 方法,当串口缓冲区有数据 ...
- C#串口SerialPort常用属性方法
SerialPort(): //属性 .BaudRate;获取或设置波特率 .BytesToRead;得到 接收到数据的字节数 .BytesToWrites;得到送往串口的字节数 .DataBits; ...
- (转)C#串口SerialPort常用属性方法
SerialPort(): //属性 .BaudRate;获取或设置波特率 .BytesToRead;得到 接收到数据的字节数 .BytesToWrites;得到送往串口的字节数 .DataBits; ...
随机推荐
- 摄像头拍照,PHP输入流php://input的使用分析
在做一个摄像头拍照然后上传的功能,php中使用php://input来获取内容.于是就了解了下php://input. 从官网信息来看,php://input是一个只读信息流,当请求方式是post的, ...
- 【C语言入门教程】目录/大纲
第一章 C语言编程基础 1.1 基本程序结构 1.2 函数库 和 链接 1.3 C语言“32个”关键字 第二章 数据类型.运算符和表达式 2.1 数据类型(5种基本数据类型),聚合类型与修饰符 2.2 ...
- BestCoder Round #90
有生以来第一场在COGS以外的地方打的比赛.挂成dog了. 主要是没有经验,加之代码能力过弱.还有最后的瞎hack三次,Too Young Too Simple...... 言归正传. (抄一发题解先 ...
- JavaScript——理解闭包及作用
js是一个函数级语言,变量的作用域是: 内部可以访问内部,内部可以访问外部,外部不能访问内部. 如果要在外部,访问函数内部的变量,就要用到闭包.闭包就是指访问到了本不该访问的变量. 闭包作用1:实现封 ...
- java2
1:关键字(掌握) (1)被Java语言赋予特定含义的单词 (2)特点: 全部小写. (3)注意事项: A:goto和const作为保留字存在. B:类似于Notepad++这样的高级记事本会对关键字 ...
- cf596d
题意:有一排等高的树木,高度都为h.给出每棵树在数轴上的坐标,每次有可能是最左边或者最右边的立着的树倒下,概率都是0.5.最终所有树都倒下.每棵树在倒下的时候有p的概率向左倒,1-p的概率向右倒.如果 ...
- 浅析HTTP协议
HTTP协议是什么 HTPP协议是一种网际层协议,HTTP协议是无状态的,HTTP协议对用户是透明的. 每一次HTTP请求都是一次事务,一个HTTP事务是由一条请求命令和一个响应结果组成的,HTTP通 ...
- ActiveMQ开发与简介
1.概述与介绍 ActiveMQ是Apache出品,最流行的.功能强大的即时通讯和集成模式的开源服务器.ActiveMQ是一个完全支持JMS1.1和J2EE1.4规范的JMSProvider实现.提供 ...
- 【转载】CentOS服务器配置VPN详解
转载来自: https://bbs.aliyun.com/read/162297.html http://www.wanghailin.cn/centos-7-vpn/ 操作系统:CentOS 6.3 ...
- 【转载】Linux常用命令列表
原文地址:http://www.cnblogs.com/Javame/p/3968343.html 1 目录与文件操作 1.1 ls(初级) 使用权限:所有人 功能 : 显示指定工作目录下之内容(列出 ...