这个问题有点一般,但在过去的几年里,我发现自己在各种情况下都遇到过这个问题,所以我认为一定有一个标准的解决方案。
我想知道是否有标准的算法来处理从某种套接字/流中读取数据的情况,使用某种专有协议,但消息不能保证以整个块到达?
我遇到过这个问题的各种不同的协议和各种套接字/流,如串口,TCP套接字,UNIX套接字和目前的蓝牙流在C#
。
为了说明这一点,我举一个简单的例子:
//A simple protocol where a message starts with a #,
//ends with a *, and has the header separated with a ;
#somemessage;somedatahere*
//A read operation on a socket may yield:
#somemessage;some //can be truncated
#somemessage;somedatahere*#someme //can be a full message with additional bytes appended
ssage;somedatahere*#somemessage;somedatahere* //prepended bytes
在过去,我已经将任何被读取的内容复制到"工作缓冲区"中,并跟踪我在该缓冲区中的索引。然后,当我找到一个完整的消息时,我将其从工作缓冲区中删除,但我以前遇到过这样的情况,即在缓冲区的前面堆积了一堆垃圾。
其他人采取什么方法?我相对没有经验,我的背景是数字编码,这从来没有出现过问题。
这个问题确实有点太一般了:答案取决于协议如何定义消息边界。AFAIU,你提到的垃圾实际上是前面消息的尾部部分;它的头部不知怎么被遗漏了。在这种情况下,您可能只会忽略所有内容,直到识别出下一条消息的开始。如果协议没有明确地分离流中的消息,通常您必须关闭连接并重新开始。
如果您想要处理符合特定语法的外部流,您应该实现一个接受该语法的解析器,并以某种方式报告语法错误。无论您是从文件读取还是从TCP/IP读取,都是如此。
您可以编写解析器,使其一次处理(接受)一个字节或字符。解析器将需要一些状态变量。其中一些将是缓冲区。
对于文本流,大多数语法可以表示为符号的排列,并使用一组规则来标识单个符号。这自然会生成一个分为两部分的解析器,其中一部分(词法分析器)用于标识符号,并传递给解析器的其余部分。
我喜欢使用准有限状态机解析器。在Java中,该解析器的基础代码如下:
public abstract class Parser
{
protected abstract class State {
public abstract State parse(char c);
public abstract void parseEOF();
}
private State currentState;
protected abstract State getStartState();
public final void parse(char c) {
currentState = currentState.parse(c);
}
public final void parseEOF() {
currentState.parseEOF();
currentState = null;
}
public final void parse(InputStream input) {
currentState = getStartState();
char c;
while((c = input.read()) != -1) {
parse(c);
}
parseEOF();
}
}