这个问题有点普遍,但我发现自己在过去几年中遇到过各种各样的情况,因此必须有一个标准的解决方案。
我想知道是否有标准算法来处理使用某种专有协议从某种套接字/流中读取数据的情况,但是消息不能保证以整个块的形式到达?
我遇到过各种不同协议和各种套接字/流的问题,例如串行端口,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
在过去,我已将所读过的内容复制到工作缓冲区"中,并跟踪我在该缓冲区中的索引。然后,当我找到一个完整的消息时,我将其从工作缓冲区中删除,但我之前已经处于这种情况,因此在缓冲区的前面会堆积大量垃圾。
其他人采取什么方法?我相对缺乏经验,我的背景是数字编码,这在以前从未出现过问题。
答案 0 :(得分:1)
问题确实有点过于通用:答案取决于协议如何定义消息边界。 AFAIU,你提到的垃圾实际上是上一条消息的尾部;它的头部已经错过了某种方式。在这种情况下,您可能只会忽略所有内容,直到识别出下一条消息的开头。如果协议没有明确区分流中的消息,通常您必须关闭连接并重新开始。
答案 1 :(得分:0)
如果要处理应符合特定语法的外部流,则应实现接受该语法的解析器,并以某种方式报告语法错误。无论您是从文件读取还是读取TCP / IP,都会出现这种情况。
您可以编写解析器,以便一次处理(接受)一个字节或字符。解析器需要一些状态变量。其中一些将是缓冲区。
对于文本流,大多数语法可以表示为符号的排列,并使用一组规则表示各个符号的标识。这自然会产生一个分解为两部分的解析器,其中一部分( lexer )用于标识符号,并将其传递给解析器的其余部分。
我喜欢使用准有限状态机解析器。在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();
}
}