处理套接字碎片输入的一般方法

时间:2014-08-26 15:29:23

标签: algorithm sockets language-agnostic stream

这个问题有点普遍,但我发现自己在过去几年中遇到过各种各样的情况,因此必须有一个标准的解决方案。

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

我遇到过各种不同协议和各种套接字/流的问题,例如串行端口,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

在过去,我已将所读过的内容复制到工作缓冲区"中,并跟踪我在该缓冲区中的索引。然后,当我找到一个完整的消息时,我将其从工作缓冲区中删除,但我之前已经处于这种情况,因此在缓冲区的前面会堆积大量垃圾。

其他人采取什么方法?我相对缺乏经验,我的背景是数字编码,这在以前从未出现过问题。

2 个答案:

答案 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();
    }
 }