WaitFor() - 如何等待特定缓冲区到达 Steam/SerialPort?

WaitFor() - How to wait for a specific buffer to arrive on Steam/SerialPort?

要求的行为:我想听听关于挂起调用线程直到在 Stream/SerialPort 上接收到特定缓冲区的建议的通用解决方案。目前,我不关心超时等问题,但是我需要一些健壮的东西。

尝试的方法:

    Class myClass
    {
        private SerialPort _port; //Assume configured and connected.

        public void WaitFor(byte[] buffer)
        {            
            int bufferLength = buffer.Length;
            byte[] comparisonBuffer = new byte[bufferLength];

            while(true)
            {
                if(_port.BytesToRead >= bufferLength)
                {
                    _port.Read(comparisonBuffer, 0, bufferLength);
                    if (comparisonBuffer.SequenceEqual(buffer)) { return; }
                }
            }
        }
     {

我在这方面取得了相当大的成功,但它只是有一种 "hacky" 的感觉。它经常给我带来麻烦。我相信这是因为我无法保证在预期数据包之前或之后都没有收到其他数据,所以自然而然地,这种方法最终可能会不同步地读取流。在这种情况下,我不想丢失 leading/trailing 数据,但该方法应该释放线程。

我需要在过程中实施,因此事件驱动方法对我来说并不适用。在一般意义上,我希望能够实现为;

    Do thing;
    WaitFor(mybuffer);
    Do other thing;

SerialPort.Read() 已经阻塞,直到至少一个字节到达。因此,您不需要(也不应该)像现在这样使用 BytesToRead - 您已经引入了一个可怕的忙等待循环。

相反,做这样的事情:

// Reads 'count' bytes from a serial port into the specified
// part of a buffer. This blocks until all the bytes have been read.

public void BlockingRead(SerialPort port, byte[] buffer, int offset, int count)
{
    while (count > 0)
    {
        // SerialPort.Read() blocks until at least one byte has been read, or SerialPort.ReadTimeout milliseconds
        // have elapsed. If a timeout occurs a TimeoutException will be thrown.
        // Because SerialPort.Read() blocks until some data is available this is not a busy loop,
        // and we do NOT need to issue any calls to Thread.Sleep().

        int bytesRead = port.Read(buffer, offset, count);
        offset += bytesRead;
        count -= bytesRead;
    }
}

以下是根据 BlockingRead():

实现原始代码的方式
public void WaitFor(SerialPort port, byte[] buffer)
{
    byte[] comparisonBuffer = new byte[buffer.Length];

    while (true)
    {
        BlockingRead(port, comparisonBuffer, 0, comparisonBuffer.Length);

        if (comparisonBuffer.SequenceEqual(buffer))
            return;
    }
}

问题

假设您等待字节模式 {1,1,1,2,2} 并且串行端口已缓冲 {1,1,1,1,2,2,5}.

您的代码读取了前 5 个字节 {1,1,1,1,2},这与模式不匹配。但是从端口读取后,您读取的数据已从缓冲区中删除并且仅包含 {2,5} 并且您永远不会获得匹配项。

解决方案

public void WaitFor( byte[ ] buffer )
{
    if ( buffer.Length == 0 )
        return;

    var q = new List<byte>( buffer.Length );

    while ( true )
    {
        var current = _reader.ReadByte();
        q.Add( (byte)current );
        // sequence match so far
        if ( q.Last == buffer[ q.Count - 1 ] )
        {
            // check for total match
            if ( q.Count == buffer.Length )
                return;
        }
        else
        {
            // shift the data
            while ( q.Any() && !q.SequenceEqual( buffer.Take( q.Count ) ) )
            {
                q.RemoveAt( 0 );
            }
        }
    }
}

你觉得这个方案怎么样?

public override byte[] WaitFor(byte[] buffer, int timeout)
{
    // List to stack stream into
    List<byte> stack = new List<byte>();
    // Index of first comparison byte
    int index = 0;
    // Index of last comparison byte
    int upperBound = buffer.Length - 1;
    // Timeout Manager
    Stopwatch Sw = new Stopwatch();

    Sw.Start();
    while (Sw.Elapsed.Seconds <= timeout)
    {
        // Read off the last byte receievd and add to the stack
        stack.Add((byte)_port.ReadByte());

        // If my stack contains enough bytes to compare to the buffer
        if (stack.Count > upperBound)
        {
            // If my first comparison byte matches my first buffer byte
            if (stack[index] == buffer[0])
            {
                // Extract the comparison block to array
                byte[] compBuffer = stack.GetRange(index,upperBound +1).ToArray();

                // If the comparison matches, break and return the redundent bytes should I wish to handle them.
                if ((compBuffer.SequenceEqual(buffer) && (index-1 > 0))) { return stack.GetRange(0, index - 1).ToArray(); }
                // If there were no redundent bytes, just return zero.
                else if (compBuffer.SequenceEqual(buffer)) { return new byte[] { 0}; }  
            }

            // Increments
            index += 1;
            upperBound += 1;
        }

    }

    throw new TimeoutException("Timeout: Expected buffer was not received prior to timeout");
}