我需要从虚拟COM端口读取数据并检测消息“Dreq”。按下连接按钮后,它连接到我的COM8端口并开始读取新线程。我还有一个断开按钮,我希望关闭读取并断开与COM8端口的连接。但是,我在关闭BeginRead时遇到问题。
public partial class Form1 : Form
{
SerialPort sp;
Stream stream;
IAsyncResult recv_result;
private void button1_Click(object sender, EventArgs e)
{
sp = new SerialPort("COM8", 9600);
sp.Open();
sp.ReadTimeout = 50000;
sp.NewLine = "\n\r\0";
stream = sp.BaseStream;
recv_result = stream.BeginRead(new byte[1], 0, 0, new
AsyncCallback(ReadCallBack), stream);
}
private void ReadCallBack(IAsyncResult ar)
{
Stream stream = (Stream)ar.AsyncState;
string temp;
while (stream.CanRead)
{
temp = sp.ReadLine();
// ... do something with temp
}
}
private void disconnectButton_Click(object sender, EventArgs e)
{
stream.EndRead(recv_result);
sp.Close();
}
}
答案 0 :(得分:2)
您可以尝试拨打sp.DiscardOutBuffer()
。它将调用您的读回调,然后您可以使用stream.EndRead()
。
private void disconnectButton_Click(object sender, EventArgs e)
{
sp.DiscardOutBuffer();
stream.EndRead(recv_result);
sp.Close();
}
答案 1 :(得分:2)
你可以尝试这个程序。
using System;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.IO;
using System.Text;
public class clsState {
private const int BUFFER_SIZE = 1024;
public byte[] BytesBuffer = new byte[BUFFER_SIZE];
public SerialPort sp;
public Stream stream;
}
public partial class Form1 : Form {
SerialPort sp;
Stream stream;
IAsyncResult recv_result;
bool endLoop = false;
Thread _readThread;
private ManualResetEvent _readDone = new ManualResetEvent(false);
private void button1_Click(object sender, EventArgs e) {
sp = new SerialPort("COM8", 9600);
sp.Open();
sp.ReadTimeout = 50000;
sp.NewLine = "\n\r\0";
stream = sp.BaseStream;
// save serial port and stream to state object
clsState state = new clsState();
state.sp = sp;
state.stream = stream;
// create worker thread
endLoop = false;
_readThread = new Thread(() => ReadThreadProc(state))
_readThread.IsBackground = true;
_readThread.Start();
}
private void ReadThreadProc(clsState state) {
while (endLoop == false){
// open and then close the gate as soon as after one thread passed
_readDone.Reset();
// starting ascynchronous read
recv_result = state.stream.BeginRead(state.BytesBuffer, 0, state.BytesBuffer.Length, new AsyncCallback(ReadCallBack), state.stream);
// worker thread block in here (waiting for... _readDone.Set())
_readDone.WaitOne();
}
}
private void ReadCallBack(IAsyncResult ar) {
string temp;
int bytesRead = 0;
// read serial port and stream from IAsyncResult
clsState state = (clsState) ar.AsyncState;
// if port serial is open and..
if (state.sp.IsOpen) {
// the stream can read then..
if(state.stream.CanRead) {
// wait for asynchronous read to completed
bytesRead = state.stream.EndRead(ar);
}
}
if(bytesRead > 0) {
// convert data in state.BytesBuffer from bytes array to string and save to temp variable
temp = Encoding.ASCII.GetString(state.BytesBuffer);
// open gate for next data
_readDone.Set();
}
}
private void disconnectButton_Click(object sender, EventArgs e) {
// ending loop and will kill worker thread...
endLoop = true;
// release begin read
_readDone.Set();
if (_readThread != null){
if (_readThread.IsAlive){ // if worker thread still live
_readThread.Join(); // wait thread off in here..
}
}
// close serial port
if (sp.IsOpen) sp.Close();
// close stream and dispose it
if (stream.CanRead || stream.CanWrite) {
stream.Close();
stream.Dispose();
}
}
}
答案 2 :(得分:0)
这是一个老帖子,但我认为这个解决方案可以帮助有同样问题的人。
我正在为应用程序使用遗留代码,我发现BeginRead和EndRead的问题在于无法取消异步操作。因此,当您关闭端口时,您对BeginRead的调用永远保持在那里,直到端口收到另一个字节,然后您对EndRead的调用将释放该端口。如果没有这种情况发生,那么您的应用程序可能会挂起,甚至任务管理器也无法关闭它,直到您拔下串口电缆!
幸运的是,TPL库可以以非常简单和优雅的方式解决这个问题。 CancelToken就是您所需要的:
打开端口:
while (x)
var myTask = _serialPort.BaseStream.ReadAsync(_serialBuffer, 0, _bufferLength, cancelToken.Token);
var bytesRead = await myTask;
... your business logic here...
if ((myTask.IsCanceled) || (myTask.IsFaulted)) break;
}
关闭港口:
cancelToken.Cancel(false);
请注意,while循环优于递归调用,因为当端口广播大量信息时,15分钟后会抛出堆栈溢出异常。
这是一个非常简单的实现。我希望它有所帮助