面试题答案
一键面试协议头设计
- 固定部分:
- 协议版本号:例如使用1个字节表示,用于标识协议的版本,方便后续升级和兼容性处理。
- 消息类型:1 - 2个字节,标识不同类型的消息,如请求消息、响应消息、心跳消息等。
- 消息长度:通常使用4个字节,以无符号整数形式表示整个消息(包括协议头和协议体)的长度,方便接收端准确读取完整消息。
- 可选部分:
- 序列号:对于需要保证顺序的消息,可使用4个字节表示序列号。
- 源节点ID:若系统需要区分不同的源节点,可使用一定字节数(如4字节)来标识。
- 目标节点ID:类似源节点ID,用于标识消息的目标节点。
协议体结构
- 根据消息类型设计:
- 请求消息:包含请求的具体参数,例如对于一个用户登录请求,可能包含用户名、密码等字段,这些字段需要定义明确的数据类型和长度。
- 响应消息:根据请求的不同,返回相应的结果,如成功标志、错误码(若失败)、返回数据等。
- 心跳消息:可能只包含一个简单的心跳标识字段,如一个固定的字节值。
- 数据类型规范:
- 对于字符串,应指定字符编码(如UTF - 8),并明确长度表示方式,如前缀表示长度或者固定长度。
- 对于数值类型,明确使用有符号还是无符号,以及字节数,如int使用4字节,long使用8字节等。
协议的编解码实现
- 编码:
- 使用Java的字节数组操作类,如
ByteBuffer
。 - 首先填充协议头,按照协议头设计的顺序和字节数依次写入版本号、消息类型等字段。
- 然后处理协议体,根据协议体结构,将各个字段转换为字节数组写入
ByteBuffer
。 - 最后,根据消息长度字段的设计,更新消息长度值,并将
ByteBuffer
中的数据转换为最终发送的字节数组。
- 使用Java的字节数组操作类,如
- 解码:
- 同样使用
ByteBuffer
,接收字节数组并包装成ByteBuffer
对象。 - 先读取协议头部分,按照协议头设计的字节数依次读取版本号、消息类型等字段。
- 根据消息长度字段,确定协议体的长度,读取协议体字节数组。
- 按照协议体结构,将字节数组转换为相应的数据类型,如将字节数组转换为字符串、数值等。
- 同样使用
协议扩展和兼容性处理
- 协议扩展:
- 增加新消息类型:在协议头的消息类型字段中预留一些值,用于未来扩展新的消息类型。当需要新功能时,定义新的消息类型,并相应设计其协议体结构。
- 协议头扩展:在协议头中预留一些字节位,可用于未来添加新的头部字段。若要添加新字段,先更新协议版本号,同时在编码和解码过程中处理新字段。
- 兼容性处理:
- 版本协商:在节点建立连接时,通过握手过程协商使用的协议版本。例如,发送端发送支持的版本列表,接收端选择一个双方都支持的版本。
- 旧版本兼容:对于旧版本节点,新节点在发送消息时,尽量保证新特性可以通过可扩展的方式处理,使得旧节点能够忽略不认识的字段而正常处理其他部分。在解码时,新节点能够处理旧版本消息的格式。例如,若旧版本没有某个协议头字段,新节点在解码时能够正确识别并填充默认值。