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

时间:2016-09-05 11:17:29

标签: c# stream serial-port tcp-ip

请求的行为:我想听听用于挂起调用线程的提议的通用解决方案,直到在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”的感觉。它经常给我带来麻烦。我相信这是因为我无法保证在预期数据包之前或之后没有收到其他数据,所以这种方法自然会最终读取流不同步。在这种情况下,我不想丢失前导/尾随数据,但方法应该释放线程。

我需要以程序性的方式实现,因此事件驱动的方法对我来说不起作用。在一般意义上,我希望能够实现;

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

3 个答案:

答案 0 :(得分:0)

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 :(得分:0)

问题

让我们假设你等待字节模式{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 );
            }
        }
    }
}

答案 2 :(得分: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");
}