停止 Stream.BeginRead()

发布于 2024-11-25 17:50:02 字数 1058 浏览 0 评论 0原文

我需要从虚拟 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();
    }
}

i need to read data from my virtual com port and detect the message "Dreq". Once i press the connect button, it connects to my COM8 port and begins reading in a new thread. I also have a disconnect button in which i wish to close the reading and disconnect from the COM8 port. However, i have problems closing the 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();
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

德意的啸 2024-12-02 17:50:02

您可以尝试调用sp.DiscardOutBuffer()。它将调用您的读取回调,然后您可以使用stream.EndRead()

private void disconnectButton_Click(object sender, EventArgs e)
{
    sp.DiscardOutBuffer();
    stream.EndRead(recv_result);
    sp.Close();
}

You can try calling sp.DiscardOutBuffer(). It will call your read callback and you can then use stream.EndRead().

private void disconnectButton_Click(object sender, EventArgs e)
{
    sp.DiscardOutBuffer();
    stream.EndRead(recv_result);
    sp.Close();
}
与酒说心事 2024-12-02 17:50:02

你可以试试这个程序。

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();
        }
    }
}

You can try this program.

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();
        }
    }
}
初吻给了烟 2024-12-02 17:50:02

这是一篇旧帖子,但我认为这个解决方案可以帮助遇到同样问题的人。

我在应用程序中使用遗留代码,发现 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 分钟后会引发堆栈溢出异常。

这是一个非常简单的实现。我希望它有帮助

It's an old post, but I think this solution can help people with the same problem.

I was using legacy code for an application and I found that the problem with BeginRead and EndRead is that there is no way to cancel the asynchronous operation. Therefore, when you close the port, your call to BeginRead stays there forever until another byte is received in the port, then your call to EndRead will free up the port. If it does not happen this way, then your application may hang and not even task manager can close it until you unplug the serial port cable!

Fortunately the TPL library can fix this problem in a very simple and elegant way. The CancelToken is what you need:

On port open:

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;
}

On port close:

cancelToken.Cancel(false);

Please note a while loop is better than recursive call because when port is broadcasting lots of information, a stack overflow exception is thrown after 15 minutes.

It's a very simple implementation. I hope it helps

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文