处理来自套接字的碎片输入的通用方法



这个问题有点一般,但在过去的几年里,我发现自己在各种情况下都遇到过这个问题,所以我认为一定有一个标准的解决方案。

我想知道是否有标准的算法来处理从某种套接字/流中读取数据的情况,使用某种专有协议,但消息不能保证以整个块到达?

我遇到过这个问题的各种不同的协议和各种套接字/流,如串口,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();
    }
 }

最新更新