Netty 基础-协议设计与解析
6. 协议设计与解析
6.1 为什么需要协议?
TCP/IP 中消息传输基于流的方式,没有边界。
协议的目的就是划定消息的边界,制定通信双方要共同遵守的通信规则
例如:在网络上传输
下雨天留客天留我不留
是中文一句著名的无标点符号句子,在没有标点符号情况下,这句话有数种拆解方式,而意思却是完全不同,所以常被用作讲述标点符号的重要性
一种解读
下雨天留客,天留,我不留
另一种解读
下雨天,留客天,留我不?留
如何设计协议呢?其实就是给网络传输的信息加上“标点符号”。但通过分隔符来断句不是很好,因为分隔符本身如果用于传输,那么必须加以区分。因此,下面一种协议较为常用
定长字节表示内容长度 + 实际内容
例如,假设一个中文字符长度为 3,按照上述协议的规则,发送信息方式如下,就不会被接收方弄错意思了
0f下雨天留客
06天留
09我不留
6.2 redis 协议举例
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;import java.nio.charset.Charset;/*** @author chenxc* @date 2021/9/7 22:26*/
@Slf4j
public class TestRedis {public static void main(String[] args) {//回车、换行NioEventLoopGroup worker = new NioEventLoopGroup();byte[] LINE = {13, 10};try {Bootstrap bootstrap = new Bootstrap();bootstrap.channel(NioSocketChannel.class);bootstrap.group(worker);bootstrap.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new LoggingHandler());ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {// 会在连接 channel 建立成功后,会触发 active 事件@Overridepublic void channelActive(ChannelHandlerContext ctx) {set(ctx);get(ctx);}private void get(ChannelHandlerContext ctx) {ByteBuf buf = ctx.alloc().buffer();buf.writeBytes("*2".getBytes());buf.writeBytes(LINE);buf.writeBytes("$3".getBytes());buf.writeBytes(LINE);buf.writeBytes("get".getBytes());buf.writeBytes(LINE);buf.writeBytes("$3".getBytes());buf.writeBytes(LINE);buf.writeBytes("aaa".getBytes());buf.writeBytes(LINE);ctx.writeAndFlush(buf);}private void set(ChannelHandlerContext ctx) {ByteBuf buf = ctx.alloc().buffer();buf.writeBytes("*3".getBytes());buf.writeBytes(LINE);buf.writeBytes("$3".getBytes());buf.writeBytes(LINE);buf.writeBytes("set".getBytes());buf.writeBytes(LINE);buf.writeBytes("$3".getBytes());buf.writeBytes(LINE);buf.writeBytes("aaa".getBytes());buf.writeBytes(LINE);buf.writeBytes("$3".getBytes());buf.writeBytes(LINE);buf.writeBytes("bbb".getBytes());buf.writeBytes(LINE);ctx.writeAndFlush(buf);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;System.out.println(buf.toString(Charset.defaultCharset()));}});}});ChannelFuture channelFuture = bootstrap.connect("localhost", 6379).sync();channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {log.error("client error", e);} finally {worker.shutdownGracefully();}}}
6.3 http 协议举例
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;import java.nio.charset.StandardCharsets;/*** @author chenxc* @date 2021/9/1 22:48*/
@Slf4j
public class TestHttp {public static void main(String[] args){final NioEventLoopGroup boos = new NioEventLoopGroup(1);final NioEventLoopGroup worker = new NioEventLoopGroup();try {final ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(boos,worker);bootstrap.channel(NioServerSocketChannel.class);bootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));//添加http协议 编、解码器ch.pipeline().addLast(new HttpServerCodec());//只有消息的类型为HttpRequest才会进入这个handler中ch.pipeline().addLast(new SimpleChannelInboundHandler<HttpRequest>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {// 获取请求log.debug("{}",msg.uri()); //请求地址log.debug("{}",msg.headers()); //请求头//返回响应DefaultFullHttpResponse response = new DefaultFullHttpResponse(msg.protocolVersion(), HttpResponseStatus.OK);final byte[] bytes = "<h1>Hello world</h1>".getBytes(StandardCharsets.UTF_8);response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH,bytes.length);response.content().writeBytes(bytes);ctx.writeAndFlush(response);}});/*ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {if (msg instanceof HttpRequest){ //请求行 请求头}else if (msg instanceof HttpContent){ //请求体}}});*/}});ChannelFuture channelFuture = bootstrap.bind(8080).sync();channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {log.error("server error {}", e.getLocalizedMessage());} finally {boos.shutdownGracefully();worker.shutdownGracefully();}}}
6.4 自定义协议要素
- 魔数,用来在第一时间判定是否是无效数据包
- 版本号,可以支持协议的升级
- 序列化算法,消息正文到底采用哪种序列化反序列化方式,可以由此扩展,例如:json、protobuf、hessian、jdk
- 指令类型,是登录、注册、单聊、群聊… 跟业务相关
- 请求序号,为了双工通信,提供异步能力
- 正文长度
- 消息正文
编解码器
根据上面的要素,设计一个登录请求消息和登录响应消息,并使用 Netty 完成收发
import com.itcxc.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;/*** 必须和LengthFieldBasedFrameDecoder一起使用,确保接到的byteBuf消息是完整的* @author chenxc* @date 2021/9/9 23:35*/
@Slf4j
@ChannelHandler.Sharable
public class MessageCodecSharable extends MessageToMessageCodec<ByteBuf, Message> {@Overrideprotected void encode(ChannelHandlerContext ctx, Message msg, List<Object> outList) throws Exception {final ByteBuf out = ctx.alloc().buffer();//1、 5个字节的魔术out.writeBytes(new byte[]{'i','t','c','x','c'});//2、 1个字节的版本out.writeByte(1);//3、 1个字节的系列化算法 jdk-0 json-1out.writeByte(0);//4、 1个字节的指令类型out.writeByte(msg.getMessageType());//5、 4个字节的请求序列out.writeInt(msg.getSequenceId());// 系列化对象ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(msg);final byte[] bytes = bos.toByteArray();//6、 4个字节的正文长度out.writeInt(bytes.length);//7、 正文out.writeBytes(bytes);outList.add(out);}@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {byte[] magcNum = new byte[5];in.readBytes(magcNum,0, 5);String res = new String(magcNum);byte version = in.readByte();byte serializableType = in.readByte();byte messageType = in.readByte();int sequenceId = in.readInt();int length = in.readInt();byte[] bytes = new byte[length];in.readBytes(bytes,0, length);Message msg = null;if (serializableType == 0){ByteArrayInputStream bis = new ByteArrayInputStream(bytes);ObjectInputStream ois = new ObjectInputStream(bis);msg = (Message) ois.readObject();}log.debug("{},{},{},{},{},{}",res,version,serializableType,messageType,sequenceId,length);log.debug("{}",msg);out.add(msg);}
}
测试
import com.itcxc.message.LoginRequestMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;/*** @author chenxc* @date 2021/9/8 22:45*/
public class TestMessageCodec {public static void main(String[] args) {EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler(LogLevel.DEBUG),new LengthFieldBasedFrameDecoder(1024,12,4,0,0),new MessageCodec());LoginRequestMessage message = new LoginRequestMessage("shangsan","123");channel.writeOutbound(message);ByteBuf buf = channel.readOutbound();//半包测试final ByteBuf buf1 = buf.slice(0, 100);buf1.retain();final ByteBuf buf2 = buf.slice(100, buf.readableBytes() - 100);buf2.retain();channel.writeInbound(buf); //引用指数会减1channel.writeInbound(buf1);channel.writeInbound(buf2);}
}
解读
Netty 基础-协议设计与解析相关推荐
- Netty协议设计与解析
Netty协议设计与解析 1. 为什么需要协议? TCP/IP 中消息传输基于流的方式,没有边界. 协议的目的就是划定消息的边界,制定通信双方要共同遵守的通信规则 例如:在网络上传输 下雨天留客天留我 ...
- 04、Netty学习笔记—(黏包半包及协议设计解析)
文章目录 一.粘包与半包 1.1.现象分析 1.1.1.粘包.半包情况分析 1.1.2.滑动窗口.MSS限制.Nagle算法介绍 1.2.粘包.半包现象复现 1.2.1.粘包复现 1.2.2.半包复现 ...
- 蚂蚁通讯框架SOFABolt之私有通讯协议设计
前言 SOFABolt 是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架. 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO ...
- Netty系列之Netty基础概念与组件
什么是Netty,Netty各个组件介绍 本部分转载自 Java技术债务[什么是Netty?为什么使用Netty?Netty有哪些组件?] 原文链接:https://blog.csdn.net/qq_ ...
- 从零实现RPC框架之:4协议设计
前言 一提到协议,最先想到的可能是 TCP 协议.UDP 协议等等,这些网络传输协议的实现以及应用层的HTTP协议. 其实rpc协议和http协议都属于应用层协议 可能你会问:"前面你不是说 ...
- 浅谈基于TCP和UDP的协议设计
From:http://blog.sina.com.cn/s/blog_48d4cf2d0101859x.html 一个基于TCP/WebSockets的超级精简的长连接消息协议:https://st ...
- Netty 高性能架构设计
Netty 高性能架构设计 Netty 概述 原生 NIO 存在的问题 Netty 官网说明 Netty 的优点 Netty 版本说明 线程模型基本介绍 传统阻塞 I/O 服务模型 Reactor 模 ...
- 开源项目SMSS发开指南(三)——protobuf协议设计
本文的第一部分将介绍protobuf使用基础以及如何利用protobuf设计通信协议.第二部分会给出smss项目的协议设计规范和源码讲解. 一.Protobuf使用基础 什么是protobuf pro ...
- 兼具高效与易用,融云 IM 即时通讯长连接协议设计思路
无论是 PC 端还是移动端,接入网络实现通信都需要建立双端的连接.关注[融云全球互联网通信云]了解更多 客户端和服务端建立连接后不断开,然后进行通信(也就是发送报文)的方式就是长连接. 与之相反,短连 ...
最新文章
- 图灵七月书讯【Cassandra权威指南将在7月末上市】
- mysql 锁-比较详细、深入的介绍
- Training—Capturing Photos
- 类和对象—对象特性—函数的分类和调用
- ZOJ 3962 Seven Segment Display(数位DP)题解
- 蓝桥杯——输出米字形
- java 链表_java数据结构与算法之顺序表与链表深入分析(一)
- 海蜘蛛理由器做端口映射
- 再探Linux内核write系统调用操作的原子性
- sklearn kfold_sklearn函数:cross_val_score(交叉验证评分)
- 云WAF之语义分析引擎
- ActiveX控件的注册方法
- 三菱四节传送带控制梯形图_四节传送带控制
- 【c语言数学函数库】
- Linux 拷贝文件
- 2003- cant connect to MYSQL server on localhost(10061)
- bootstrap可视化布局(免费,自定义,方便下载)网页自定义,后台、前端页面自定义
- 人类Humankind for Mac(历史战略游戏)
- java语言多态性的表现形式_[Java教程]多态性的表现形式
- 【工具使用系列】关于 MATLAB Simulink 物理建模,你需要知道的事
热门文章
- 如何禁止WordPress站点前端显示管理工具栏?附3种方法
- leetcode每日一题——T70. 爬楼梯(易):斐波那契公式
- 为什么普通人在互联网上赚钱总是遇到坑?这是我的几条建议
- 一文读懂数仓建设和数据治理
- (程序猿专属)1024-我用代码写成浪漫情话表白你
- Ptyhon xlrd常用函数用法介绍
- python加密敏感信息_小技巧 | 用python给敏感信息加水印
- 2016年4月重庆计算机一级考试,2016年4月重庆市计算机一级机试题2
- 困惑我们人生的60个问题的答案--言简意赅的开心果,说的多好啊
- java·mysql编写快递e站
Netty协议设计与解析 1. 为什么需要协议? TCP/IP 中消息传输基于流的方式,没有边界. 协议的目的就是划定消息的边界,制定通信双方要共同遵守的通信规则 例如:在网络上传输 下雨天留客天留我 ...
文章目录 一.粘包与半包 1.1.现象分析 1.1.1.粘包.半包情况分析 1.1.2.滑动窗口.MSS限制.Nagle算法介绍 1.2.粘包.半包现象复现 1.2.1.粘包复现 1.2.2.半包复现 ...
前言 SOFABolt 是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架. 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO ...
什么是Netty,Netty各个组件介绍 本部分转载自 Java技术债务[什么是Netty?为什么使用Netty?Netty有哪些组件?] 原文链接:https://blog.csdn.net/qq_ ...
前言 一提到协议,最先想到的可能是 TCP 协议.UDP 协议等等,这些网络传输协议的实现以及应用层的HTTP协议. 其实rpc协议和http协议都属于应用层协议 可能你会问:"前面你不是说 ...
From:http://blog.sina.com.cn/s/blog_48d4cf2d0101859x.html 一个基于TCP/WebSockets的超级精简的长连接消息协议:https://st ...
Netty 高性能架构设计 Netty 概述 原生 NIO 存在的问题 Netty 官网说明 Netty 的优点 Netty 版本说明 线程模型基本介绍 传统阻塞 I/O 服务模型 Reactor 模 ...
本文的第一部分将介绍protobuf使用基础以及如何利用protobuf设计通信协议.第二部分会给出smss项目的协议设计规范和源码讲解. 一.Protobuf使用基础 什么是protobuf pro ...
无论是 PC 端还是移动端,接入网络实现通信都需要建立双端的连接.关注[融云全球互联网通信云]了解更多 客户端和服务端建立连接后不断开,然后进行通信(也就是发送报文)的方式就是长连接. 与之相反,短连 ...