对比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; ...
随机推荐
- Go - 字典(map)
字典是一种内置的数据结构,用来保存 键值对 的 无序集合. (1)字典的创建 1) make(map[KeyType] ValueType, initialCapacity) 2) make(map[ ...
- Shell入门教程:命令替换 $() 和 ``
所谓命令替换,是把命令执行后的标准输出放入变量中.这是一个十分有威力的功能.例如说, 想查看工作目录中所有的文件名,可执行ls命令,但如何把这些文件名存入变量中,供往后的程序代码再利用呢? 使用命令替 ...
- JS实现图片预加载无需等待
网站开发时经常需要在某个页面需要实现对大量图片的浏览;用javascript来实现一个图片浏览器,让用户无需等待过长的时间就能看到其他图片 网站开发时经常需要在某个页面需要实现对大量图片的浏览,如果考 ...
- nginx实现请求转发
反向代理适用于很多场合,负载均衡是最普遍的用法. nginx 作为目前最流行的web服务器之一,可以很方便地实现反向代理. nginx 反向代理官方文档: NGINX REVERSE PROXY 当在 ...
- javascript——三元操作符
{{C?A:B}} C条件成立则为A,不存在取B 比如在跟后台交互时,有许多要设默认值 <script type=''text/javascript> var value = docume ...
- 【XLL 框架库函数】 TempActiveRef/TempActiveRef12
[XLL 框架库函数] TempActiveRef/TempActiveRef12 创建一个包含所有激活工作表引用区域 XLOPER/XLOPER12 LPXLOPER TempActiveRef(B ...
- SpringMVC拦截器
springmvc的拦截器 需求:进行用户的访问控制,判断用户是否登陆,如果登陆进行正常访问,如果没有登陆跳转到登陆页面. 1自定义拦截器类 package org.guangsoft.utils; ...
- squid介绍及其简单配置
squid的简单介绍 squid的概念 squid是一种用来缓存Internet数据的软件.接受来自人们需要下载的目标(object)的请求并适当的处理这些请求.也就是说,如果一个人想下载一web界面 ...
- 安天AVLTeam送福利喽~~
#福利来了# duang~duang~duang~ 安小天帮你辨别短信真伪!!! 是不是经常收到真假难辨的[疑似诈骗短信]是真的?是假的? 傻傻分不清楚 现在不用怕啦!!! 遇到这种情况,只需手机截 ...
- FTP协议及工作原理
1. FTP协议 什么是FTP呢?FTP 是 TCP/IP 协议组中的协议之一,是英文File Transfer Protocol的缩写. 该协议是Internet文件传送的基础,它由一系列规格说明文 ...