资讯专栏INFORMATION COLUMN

数据帧及消息定界

bang590 / 674人阅读

摘要:成帧解决接收端如何定位消息的首尾位置的问题。无论信息是编码成文本多字节二进制数或是两者结合,应用程序协议必须制定消息的接收者如何确定何时消息已完整接收。发送者先要确定消息的长度,将长度信息写入一个整数,作为消息的前缀。

framing

成帧(framing):解决接收端如何定位消息的首尾位置的问题。

无论信息是编码成文本、多字节二进制数、或是两者结合,应用程序协议必须制定消息的接收者如何确定何时消息已完整接收。

如果是采用DatagramPacket发送,则没有问题,因为它有一个确定的长度告诉接收者,让其知道消息的结束位置。

如果是通过TCP传送,则比较复杂,因为TCP协议中没有边界的概念。

变长消息

如果一个消息中的所有字段都有固定的长度,同时每个消息又是由固定数量的字段组成的话,消息的长度就能够确定,接收者就可以简单地讲消息长度对应的字节数读到一个byte[]缓存区中。
如果消息的长度是可变的,则我们无法事先知道需要读取多少字节。

如果接收者试图从套接字中读取比消息本身更多的字节,将可能发生以下两种情况之一:
1)如果信道中没有其他消息,接收者将阻塞等待,同时无法处理接收到的消息,如果发送者也在等待接收端的响应信息,则会造成死锁
2)如果信道中还有其他消息,则接收者会将后面消息的一部分甚至全部读到第一条消息中去,这将产生一些协议错误。

消息结束标识

主要有两个技术使接收者能够准确地找到消息的结束位置:

1)基于定界符(Delimiter-based)

消息的结束由一个唯一的标记指出,即发送者在传输完数据后显式添加的一个特殊字符序列。这个特殊标记不能在传输的数据中出现。特殊情况是,可以用在TCP连接上传输的最后一个消息上,发送完这个消息后,发送者就简单地关闭发送端的TCP连接,接收者读取完这条消息的最后一个字节后,将接收到熬一个流结束标记,即read返回-1,该标记指出已经读取到达了消息的末尾。
通常用在以文本方式编码的消息中,定义一个特殊的字符或字符串来标识消息的结束。

 public byte[] nextMsg() throws IOException {
        ByteArrayOutputStream messageBuffer = new ByteArrayOutputStream();
        int nextByte;
        // fetch bytes until find delimiter
        while ((nextByte = in.read()) != DELIMITER) {
            if (nextByte == -1) { // end of stream?
                if (messageBuffer.size() == 0) { // if no byte read
                    return null;
                } else { // if bytes followed by end of stream: framing error
                    throw new EOFException("Non-empty message without delimiter");
                }
            }
            messageBuffer.write(nextByte); // write byte to buffer
        }
        return messageBuffer.toByteArray();
    }
2)显示长度(Explicit length)

在变长字段或消息前附加一个固定大小的字段,用来指定字段或消息中包含了多少字节。发送者先要确定消息的长度,将长度信息写入一个整数,作为消息的前缀。消息的长度上限定义了用来编码消息长度所需要的字节数,如果消息的长度小于256字节,则需要1个字节,如果消息的长度小于65536字节,则需要2个字节。

public byte[] nextMsg() throws IOException {
        int length;
        try {
            length = in.readUnsignedShort(); // read 2 bytes
        } catch (EOFException e) { // no (or 1 byte) message
            return null;
        }
        // 0 <= length <= 65535
        byte[] msg = new byte[length];
        in.readFully(msg); // if exception, it"s a framing error.
        return msg;
    }

readFully方法将阻塞等待,直到给定的数组完全填满。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65916.html

相关文章

  • 基于 Netty 的可插拔业务通信协议的实现「1」协议描述及基本消息对象设计

    摘要:基本消息对象的设计消息对象的设计主要由两部分组成特定数据帧对应的特定消息对象。该类包含上节数据帧主帧及子帧的所有公共信息,仅仅未包含子帧中的数据体信息,该需求由基本消息对象的子类实现。 开发工程中,有一个常见的需求:服务端程序和多个客户端程序通过 TCP 协议进行通信,通信双方需通信的消息种类众多,并且客户端的数量可能有数万个。为此,双方需要约定尽可能丰富、灵活的数据帧「数据包」协议,...

    Barry_Ng 评论0 收藏0
  • PHP基础系列之正则表达式(一)

    摘要:正则表达式作为一个匹配的模板,是由定界符,原子普通字符,例如有特殊功能的字符称为元字符,例如等以及模式修正符等部分组成的文字模式。正则表达式中可以使用编码。限定符限定符用来指定正则表达式的一个给定原子必须要出现多少次才能满足匹配。 正则表达式的定义 正则表达式就是描述字符排列模式的一种自定义的语法规则。由于正则表达式本身具有一套非常完整的、可以编写模式的语法体系,提供了一种灵活且直观的...

    Anchorer 评论0 收藏0
  • 《Java编程思想》笔记13.字符串

    摘要:的构造器经过重载可以接受多种输出目的地,不过最常用的还是和。组号为表示整个表达式,组号表示被第一对括号括起的组,依此类推。有多个重载的构造器,可以接受和对象。 点击进入我的博客 字符串操作是计算机程序设计中最常见的行为 13.1 不可变String String底层是由char[]实现的,是不可变的。看起来会改变String的方法,实际上都是创建了一个新的String对象,任何指向它...

    since1986 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<