1

我正在与通过 Wifi 将数据流式传输到我的应用程序的硬件设备进行交互。数据流的很好。数据包含一个字符标题 (DATA:),表示新记录已开始。问题是我收到的数据不一定落在标题边界上,所以我必须捕获数据,直到我捕获的数据包含标题。然后,标题之前的所有内容都会进入上一个记录,而它之后的所有内容都会进入新记录。我有这个工作,但想知道是否有人以前做过这个并且有一个很好的计算机科学方法来解决这个问题。

这就是我所做的:

  1. 将当前读取的 NSData 转换为 NSString

  2. 将 NSString 附加到占位符字符串

  3. 检查标题的占位符字符串 (DATA:)。如果标题不存在,则等待下一次读取。

  4. 如果标头存在,则将其前面的任何内容附加到先前的记录占位符,并将该占位符作为完整记录传递给数组,我可以进一步解析为字段。

  5. 获取标题之后显示的任何内容并将其放在记录占位符中,以便可以在下一次读取时将其附加到。重复步骤 3 - 5。

如果您发现此方法有任何缺陷或有更好的方法建议,请告诉我。

似乎应该有一些设计模式,但我想不出一个。

谢谢。

更新:这是一些代码:

uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len) {    
    [data appendBytes:(const void *)buf length:len];
    int bytesRead;
    bytesRead += len;
} else {
    NSLog(@"No data.");
}

那么如何更改此代码以实现有限状态机?

4

2 回答 2

1

这似乎是我会怎么做的。我唯一可能做的不同的事情是编写一个为我NSData进行线性搜索的类别DATA:,只是为了节省将其转换为字符串的开销。做起来也不会那么难。就像是:

@interface NSData (Search)

- (NSRange) rangeOfData:(NSData *)aData;

@end

@implementation NSData (Search)

- (NSRange) rangeOfData:(NSData *)aData {
  const void * bytes = [self bytes];
  NSUInteger length = [self length];

  const void * searchBytes = [aData bytes];
  NSUInteger searchLength = [aData length];
  NSUInteger searchIndex = 0;

  NSRange foundRange = {NSNotFound, searchLength};
  for (NSUInteger index = 0; index < length; index++) {
    if (bytes[index] == searchBytes[searchIndex]) {
      //the current character matches
      if (foundRange.location == NSNotFound) {
        foundRange.location = index;
      }
      searchIndex++;
      if (searchIndex >= searchLength) { return foundRange; }
    } else {
      searchIndex = 0;
      foundRange.location = NSNotFound;
    }
  }
  return foundRange;
}

@end

然后你可以使用:

NSData * searchData = [@"DATA:" dataUsingEncoding:NSUTF8StringEncoding];
while(receivingData) {
  if ([receivedData rangeOfData:searchData].location != NSNotFound) {
    //WOOO!
  }
}

(警告:在浏览器中输入)

于 2010-03-16T23:50:28.830 回答
1

这是一个经典的有限状态机问题。许多基于流的数据协议可以用有限状态机来描述。

基本上你有一个状态和过渡。Boost 有一个有限的状态机库,但它可能有点矫枉过正。您可以将其实现为开关。

while(stream.hasData) {
char nextInput = stream.get();
switch(currentState) {
  case D: {
     if(nextInput == A)
       currentState = A;
     else
       currentState = D; //die 
  } case A: {
    //Same for A
  }
}
}

要求详细说明:
基本上看下图……它是一个有限状态机。在任何给定时间,机器都处于一种状态。每次将字符输入状态机时,都会进行一次转换,并且当前状态会移动。(可能回到相同的状态)。因此,您所要做的就是将网络数据建模为有限状态机,然后实现该机器。有一些库可以为您安排,然后您所要做的就是准确地实现每次转换时发生的事情。对您来说,您可能是指解释或保存数据字节。解释取决于什么过渡。转换取决于当前状态和当前输入。这是一个示例 FSM。

替代文字 http://www.freeimagehosting.net/uploads/b1706f2a8d.png
请注意,如果输入字符 DATA:,则状态将移至最后一个圆圈。任何其他序列都会将状态保持在前 5 个状态之一。(顶行)您也可以进行拆分。所以 FSM 可以做出决定,所以如果你得到一个像 DATA2: 这样的序列,那么你可以从那台机器分支到 data2: 部分,并在机器的一个完全不同的部分进行不同的解释。

于 2010-03-16T23:51:02.150 回答