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

C# WaitFor()-如何等待特定缓冲区到达Steam/SerialPort?,c#,stream,serial-port,tcp-ip,C#,Stream,Serial Port,Tcp Ip,请求的行为:我希望听到建议的通用解决方案,用于在流/串行端口上收到特定缓冲区之前挂起调用线程。目前,我不关心超时等问题,但是我需要一些健壮的东西 尝试的方法: Class myClass { private SerialPort _port; //Assume configured and connected. public void WaitFor(byte[] buffer) {

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

尝试的方法:

    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; }
                }
            }
        }
     {
我已经在这方面取得了相当大的成功,但它只是有一种“黑客”的感觉。这经常给我带来麻烦。我相信这是因为我不能保证在预期数据包之前或之后都不会收到其他数据,所以这种方法自然会导致读取流不同步。在这种情况下,我不希望丢失前导/尾随数据,但该方法应该释放线程

我需要以过程性的方式实现,这样事件驱动的方法就不会真正适合我。在一般意义上,我希望能够实现为

    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()
实现原始代码:

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

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

解决方案
public void WaitFor(字节[]缓冲区)
{
if(buffer.Length==0)
返回;
var q=新列表(buffer.Length);
while(true)
{
var current=_reader.ReadByte();
q、 添加((字节)当前值);
//目前为止的序列匹配
if(q.Last==缓冲区[q.Count-1])
{
//检查是否完全匹配
if(q.Count==缓冲区长度)
返回;
}
其他的
{
//转移数据
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");
}
公共覆盖字节[]WaitFor(字节[]缓冲区,int超时)
{
//要将流堆栈到的列表
列表堆栈=新列表();
//第一个比较字节的索引
int指数=0;
//最后一个比较字节的索引
int upperBound=buffer.Length-1;
//超时管理器
秒表Sw=新秒表();
Sw.Start();
while(Sw.eassed.Seconds上限)
{
//如果我的第一个比较字节与我的第一个缓冲区字节匹配
if(堆栈[索引]==缓冲区[0])
{
//将比较块提取到数组
字节[]compBuffer=stack.GetRange(索引,上限+1).ToArray();
//如果比较匹配,请中断并返回我希望处理的redundent字节。
if((compBuffer.SequenceEqual(buffer)和&(index-1>0)){return stack.GetRange(0,index-1).ToArray();}
//如果没有冗余字节,只返回零。
else if(compBuffer.SequenceEqual(buffer)){返回新字节[]{0};}
}
//增量
指数+=1;
上限+=1;
}
}
抛出新的TimeoutException(“超时:在超时之前未收到预期的缓冲区”);
}

好的,谢谢。通过testoread不断地重新评估
,但您在这里编写的内容并没有提供我要求的功能。假设输入函数时端口上有10个可用字节,因此count=10。在结束时,
count-=bytesRead
将计数设为0,并释放循环。现在,不管我收到了什么,我不再等待。您的函数只执行
port.Read()
@GeorgeKerwood是的,它读取您要求它读取的字节数,即传入的
count
参数
SerialPort.Read()
仅读取调用时可用的字节数。但是,重新阅读您的问题,您所要求的似乎有所不同……看起来您正在尝试读取
count
字节块,直到一个块与目标数组的内容匹配为止-是否正确?你会扔掉任何不匹配的积木。是的,对不起,我看到了。为了实现这一点,我还可以配置
ReceivedBytesThreshold
属性wait for the
DataReceived
事件。我实际上是想在传入缓冲区中识别一个特定的数据包,并在接收到它时释放它。无论如何谢谢你!你就在那里,是的。但让事情复杂化的是,我不想扔掉数据:P这就是问题所在!谢谢我开始这样想(阅读上一个答案的最后一条评论)。非常感谢。@GeorgeKerwood但是你可能跳过的所有字节呢?你现在不是把它们扔掉了吗?如果它们不重要,为什么会被传播?@MatthewWatson这应该是另一个问题的一部分:o)我同意,但这看起来很像一个X-Y问题——他所要求的解决方案似乎是错误的,我怀疑这是因为有更深层次的不适。@Rufo爵士想,先生?非常感谢。此方法只等待一个字节,而不等待整个序列。IMHO不会回答你的问题。如果你想查看代码,你应该向我道歉,错过了我的while循环。啊,我不知道codereview,谢谢你的提示。祝你过得愉快。
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");
}