其实深入浅出Netty通讯协议支持解析:第9篇教程的问题并不复杂,但是又很多的朋友都不太了解,因此呢,今天小编就来为大家分享深入浅出Netty通讯协议支持解析:第9篇教程的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!
第一种方法使用自定义协议。传输消息时,消息的前几位(如2位)是自定义的(如AB)。解码器解析时,前两位是AB,表示协议类型。CD是一种协议类型。该方法没有使用protobuf,而是直接使用Netty的自定义协议来解决问题。第二种方法是使用protobuf实现的,它实际上规定了消息是如何定义的。因为netty本身,客户端和服务器建立TCP连接,一方必须确定另一方发送的对象类型。
Protocol Buffers实现netty的多种传输协议
我们知道要使用Protocol Buffers首先要定义一个.proto文件
定义最外层消息。最外面的消息(MyMessage) 包含所有已传递的消息类型。所有消息类型都嵌套在最外面的消息类型中。每次投递都会投递特定的消息类型(以最外层消息类型的枚举类型传递)
语法="proto2";
com.zhihao.miao.netty.sixtheexample 包;
选项optimize_for=SPEED;
option java_package="com.zhihao.miao.netty.seventhexample";
选项java_outer_classname="MyDataInfo";
消息我的消息{
枚举数据类型{
人员类型=1;
狗类型=2;
猫类型=3;
}
所需数据类型data_type=1;
//oneof表示:如果有多个可选字段,某一时刻只能设置一个值,这样可以节省内存空间。
其中一个数据体{
人人=2;
狗狗=3;
猫猫=4;
}
}
给人们留言{
可选字符串名称=1;
可选int32 年龄=2;
可选字符串地址=3;
}
消息狗{
可选字符串名称=1;
可选字符串年龄=2;
}
消息猫{
可选字符串名称=1;
可选字符串城市=2;
}使用编译器编译生成代码
protoc --java_out=src/main/java src/protobuf/People.proto 关于proto协议中Oneof的含义,如果有多个可选字段,某一时刻只能设置一个值,官方链接,生成MyDataInfo类,类代码太多,这里就不贴了。
服务器代码:
com.zhihao.miao.netty.seventheexample 包;
导入io.netty.bootstrap.ServerBootstrap;
导入io.netty.channel.ChannelFuture;
导入io.netty.channel.EventLoopGroup;
导入io.netty.channel.nio.NioEventLoopGroup;
导入io.netty.channel.socket.nio.NioServerSocketChannel;
导入io.netty.handler.logging.LogLevel;
导入io.netty.handler.logging.LoggingHandler;
公共类测试服务器{
公共静态无效主(字符串[] args)抛出异常{
EventLoopGroup bossGroup=new NioEventLoopGroup();
EventLoopGroupworkerGroup=new NioEventLoopGroup();
尝试{
ServerBootstrap serverBootstrap=new ServerBootstrap();
serverBootstrap.group(bossGroup,wokerGroup).channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new TestServerInitializer());
ChannelFuturechannelFuture=serverBootstrap.bind(8888).sync();
ChannelFuture.channel().closeFuture().sync();
}最后{
bossGroup.shutdownGracely();
优雅地关闭();
}
}
}服务器初始化链接:
com.zhihao.miao.netty.seventheexample 包;
导入io.netty.channel.ChannelInitializer;
导入io.netty.channel.ChannelPipeline;
导入io.netty.channel.socket.SocketChannel;
导入io.netty.handler.codec.protobuf.ProtobufDecoder;
导入io.netty.handler.codec.protobuf.ProtobufEncoder;
导入io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
导入io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
公共类TestServerInitializer 扩展ChannelInitializer{
@覆盖
protected void initChannel(SocketChannel ch) 抛出异常{
ChannelPipeline 管道=ch.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
//使用最外层的消息实例
pipeline.addLast(new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new TestServerHandler());
}
}其实实现的关键就在于此,使用MyDataInfo.MyMessage实数列(MyDataInfo.MyMessage是枚举类型),而我们定义的三个对象恰好是它的枚举对象。
自定义服务器端Handler根据通道中传入的数据的不同dataType值来解析具体类型:
com.zhihao.miao.netty.seventheexample 包;
导入io.netty.channel.ChannelHandlerContext;
导入io.netty.channel.SimpleChannelInboundHandler;
公共类TestServerHandler 扩展SimpleChannelInboundHandler{
@覆盖
protected void channelRead0(ChannelHandlerContext ctx, MyDataInfo.MyMessage msg) 抛出异常{
MyDataInfo.MyMessage.DataType dataType=msg.getDataType();
if(dataType==MyDataInfo.MyMessage.DataType.PeopleType){
MyDataInfo.People people=msg.getPeople();
System.out.println(people.getName());
System.out.println(people.getAge());
System.out.println(people.getAddress());
}否则if(dataType==MyDataInfo.MyMessage.DataType.DogType){
MyDataInfo.Dog 狗=msg.getDog();
System.out.println(dog.getName());
System.out.println(dog.getAge());
}否则if(dataType==MyDataInfo.MyMessage.DataType.CatType){
MyDataInfo.Cat cat=msg.getCat();
System.out.println(cat.getName());
System.out.println(cat.getCity());
}
}
}客户端代码:
com.zhihao.miao.netty.seventheexample 包;
导入io.netty.bootstrap.Bootstrap;
导入io.netty.channel.ChannelFuture;
导入io.netty.channel.EventLoopGroup;
导入io.netty.channel.nio.NioEventLoopGroup;
导入io.netty.channel.socket.nio.NioSocketChannel;
公共类测试客户端{
公共静态无效主(字符串[] args)抛出异常{
EventLoopGroup eventLoopGroup=new NioEventLoopGroup();
尝试{
Bootstrap bootstrap=new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.handler(new TestClientInitializer());
ChannelFuturechannelFuture=bootstrap.connect("localhost",8888).sync();
ChannelFuture.channel().closeFuture().sync();
}最后{
eventLoopGroup.shutdownGraceively();
}
}
}客户端初始化链接:
com.zhihao.miao.netty.seventheexample 包;
导入io.netty.channel.ChannelInitializer;
导入io.netty.channel.ChannelPipeline;
导入io.netty.channel.socket.SocketChannel;
导入io.netty.handler.codec.protobuf.ProtobufDecoder;
导入io.netty.handler.codec.protobuf.ProtobufEncoder;
导入io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
导入io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
公共类TestClientInitializer 扩展ChannelInitializer{
@覆盖
protected void initChannel(SocketChannel ch) 抛出异常{
ChannelPipeline 管道=ch.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
//使用最外层的消息实例
pipeline.addLast(new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new TestClientHandler());
}
自定义处理器端的handler,随机发送不同协议的数据:
com.zhihao.miao.netty.seventheexample 包;
导入io.netty.channel.ChannelHandlerContext;
导入io.netty.channel.SimpleChannelInboundHandler;
导入java.util.Random;
公共类TestClientHandler 扩展SimpleChannelInboundHandler{
@覆盖
protected void channelRead0(ChannelHandlerContext ctx, MyDataInfo.MyMessage msg) 抛出异常{
}
//客户端向服务器发送数据
公共无效channelActive(ChannelHandlerContext ctx)抛出异常{
int randomInt=new Random().nextInt(3);
MyDataInfo.MyMessage myMessage=null;
if(0==randomInt){
myMessage=MyDataInfo.MyMessage.newBuilder()。
setDataType(MyDataInfo.MyMessage.DataType.PeopleType)。
setPeople(MyDataInfo.People.newBuilder().setName("张三").
setAddress("上海").setAge(26).build()).build();
}否则if(1==randomInt){
myMessage=MyDataInfo.MyMessage.newBuilder()。
setDataType(MyDataInfo.MyMessage.DataType.DogType)。
setDog(MyDataInfo.Dog.newBuilder().setName("旺财")
.setAge("2").build()).build();
}否则if(2==randomInt){
myMessage=MyDataInfo.MyMessage.newBuilder()。
setDataType(MyDataInfo.MyMessage.DataType.CatType)。
setCat(MyDataInfo.Cat.newBuilder().setName("汤姆")
.setCity("上海").build()).build();
}
ctx.channel().writeAndFlush(myMessage);
}
}启动服务器,然后启动客户端并执行几次。服务器控制台显示:
2017 年7 月5 日10:10:37 下午io.netty.handler.logging.LoggingHandler 通道读取
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] READ: [id:0x82a26e9f,L:/127。 0.0.1:8888-R:/127.0.0.1:51777]
2017 年7 月5 日10:10:37 下午io.netty.handler.logging.LoggingHandler channelReadComplete
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] 读取完成
汤姆
上海
2017 年7 月5 日10:11:38 下午io.netty.handler.logging.LoggingHandler 通道读取
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] READ: [id:0x128da3e7,L:/127.0 。 0.1:8888-R:/127.0.0.1:52049]
2017 年7 月5 日10:11:38 下午io.netty.handler.logging.LoggingHandler channelReadComplete
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] 读取完成
张三
26
上海
2017 年7 月5 日10:11:49 下午io.netty.handler.logging.LoggingHandler channelRead
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] READ: [id:0xa8220c73,L:/127。 0。 0.1:8888-R:/127.0.0.1:52097]
2017 年7 月5 日10:11:49 下午io.netty.handler.logging.LoggingHandler channelReadComplete
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] 读取完成
汤姆
上海
2017 年7 月5 日10:11:55 下午io.netty.handler.logging.LoggingHandler 通道读取
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] READ: [id:0x9ac52ec1,L:/127.0。 0.1:8888-R:/127.0.0.1:52125]
2017 年7 月5 日10:11:55 下午io.netty.handler.logging.LoggingHandler channelReadComplete
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] 读取完成
张三
26
上海
2017 年7 月5 日10:12:07 下午io.netty.handler.logging.LoggingHandler 通道读取
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] READ: [id:0x797d03b6,L:/127。 0.0.1:8888-R:/127.0.0.1:52178]
2017 年7 月5 日10:12:07 下午io.netty.handler.logging.LoggingHandler channelReadComplete
消息: [id:0xd5f957bd,L:/0:0:0:0:0:0:0:0:8888] 读取完成
繁荣
2
使用netty实现多种传输协议
类似官网的demo。我写了很久,在写这个demo之前也参考了官网。我对netty的理解又加深了:
三个协议实体类:
人员协议
包com.zhihao.miao.test.day10;
公共类人{
私有字符串用户名;
普里
vate int age; //get set方法 }Dog协议 package com.zhihao.miao.test.day10; public class Dog { private String name; private String age; //get set方法 }Cat协议 package com.zhihao.miao.test.day10; public class Cat { private String name; private String city; //get set方法 }服务端: import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; public class MultiServer { public static void main(String args[]) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); // 指定socket的一些属性 serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024); serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 指定是一个NIO连接通道 .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ServerChannelInitializer()); // 绑定对应的端口号,并启动开始监听端口上的连接 Channel ch = serverBootstrap.bind(8899).sync().channel(); // 等待关闭,同步端口 ch.closeFuture().sync(); } }服务器端初始化lInitializer import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; public class ServerChannelInitializer extends ChannelInitializer{ @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //解析handler pipeline.addLast(new ServlerDecoder()); pipeline.addLast(new TestServerHandler()); } }服务端解码器Handler,如果解析的位置数据是0则按照 Person协议进行解码,如果传递的位置数据是1,则按照Dog协议进行解码,如果传递的位置数据是2,则按照Cat协议进行解码: public class ServlerDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, Listout) throws Exception { int flag = in.readInt(); if(flag == 0){ int usernamelength = in.readInt(); byte[] usernamebys = new byte[usernamelength]; in.readBytes(usernamebys); String username = new String(usernamebys); int age = in.readInt(); Person pserson = new Person(); pserson.setUsername(username); pserson.setAge(age); out.add(pserson); } if(flag ==1){ int namelength =in.readInt(); byte[] namebys = new byte[namelength]; in.readBytes(namebys); String name = new String(namebys); byte[] agebys = new byte[in.readableBytes()]; in.readBytes(agebys); String age = new String(agebys); Dog dog = new Dog(); dog.setName(name); dog.setAge(age); out.add(dog); } if(flag ==2){ int namelength = in.readInt(); byte[] nameByte = new byte[namelength]; in.readBytes(nameByte); String name = new String(nameByte); byte[] colorbys = new byte[in.readableBytes()]; in.readBytes(colorbys); String color = new String(colorbys); Cat cat = new Cat(); cat.setName(name); cat.setColor(color); out.add(cat); } }自定义服务器端Handler: import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class TestServerHandler extends SimpleChannelInboundHandler{ @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if(msg instanceof Person){ System.out.println(((Person) msg).getUsername()); System.out.println(((Person) msg).getAge()); } if(msg instanceof Dog){ System.out.println(((Dog) msg).getName()); System.out.println(((Dog) msg).getAge()); } if(msg instanceof Cat){ System.out.println(((Cat) msg).getName()); System.out.println(((Cat) msg).getColor()); } } }客户端: import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; public class MultiClient { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class).handler(new ClientChannelInitializer()); // Start the connection attempt. Channel ch = b.connect("127.0.0.1", 8899).sync().channel(); ch.flush(); } }客户端初始化Initializer import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import java.util.Random; public class ClientChannelInitializer extends ChannelInitializer{ @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); int randomInt = new Random().nextInt(3); /** * 编码动作,如果随机参数是1,则传输Person协议,如果随机参数是2,则传递Dog协议, * 如果随机参数是3,则传递Cat协议 * * Person协议就是传递一个标识位为0,然后将Person对象编码成二进制传输 * Dog协议传递一个标识位为1,然后将Dog对象编码成二进制进行传输 * Cat协议传递一个标识为2,然后将Cat对象编码成二进制进行传输 */ if(0 == randomInt){ pipeline.addLast(new PersonEncoder()); Person person = new Person(); person.setUsername("zhihao"); person.setAge(27); pipeline.addLast(new TestClientHandler(person)); } if(1 == randomInt){ pipeline.addLast(new DogEncoder()); Dog dog = new Dog(); dog.setName("wangcai"); dog.setAge("2"); pipeline.addLast(new TestClientHandler(dog)); } if(2 == randomInt){ pipeline.addLast(new CatEncoder()); Cat cat = new Cat(); cat.setName("maomi"); cat.setColor("yellow"); pipeline.addLast(new TestClientHandler(cat)); } } }三种自定义编码协议,与服务器端进行对应传输Person数据的时候,在Person数据之前加上标识位置数据0,在Dog数据之前加上标识位置数据1,在Cat数据之前加上标识位置数据2,然后将其与本身的数据一起编码成二进制进行传输。 import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; public class PersonEncoder extends MessageToByteEncoder{ @Override protected void encode(ChannelHandlerContext ctx, Person msg, ByteBuf out) throws Exception { String username = msg.getUsername(); int usernamelength = username.length(); int age = msg.getAge(); out.writeInt(0); //标识位 out.writeInt(usernamelength); out.writeBytes(username.getBytes()); out.writeInt(age); } }Dog协议编码器 import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; public class DogEncoder extends MessageToByteEncoder{ @Override protected void encode(ChannelHandlerContext ctx, Dog msg, ByteBuf out) throws Exception { String name = msg.getName(); int namelength = name.length(); String age = msg.getAge(); out.writeInt(1); //标识位 out.writeInt(namelength); out.writeBytes(name.getBytes()); out.writeBytes(age.getBytes()); } }Cat协议编码器: import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; public class CatEncoder extends MessageToByteEncoder{ @Override protected void encode(ChannelHandlerContext ctx, Cat msg, ByteBuf out) throws Exception { String name = msg.getName(); int namelength = name.length(); String color = msg.getColor(); out.writeInt(2); //标识位 out.writeInt(namelength); out.writeBytes(name.getBytes()); out.writeBytes(color.getBytes()); } }自定义客户端处理器: import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class TestClientHandler extends ChannelInboundHandlerAdapter { private Person person; private Cat cat; private Dog dog; public TestClientHandler(Person person){ this.person = person; } public TestClientHandler(Dog dog){ this.dog = dog; } public TestClientHandler(Cat cat){ this.cat =cat; } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { if(person != null){ ctx.channel().writeAndFlush(person); } if(dog != null){ ctx.channel().writeAndFlush(dog); } if(cat != null){ ctx.channel().writeAndFlush(cat); } } }启动服务端,再多次启动客户端,服务器控制台打印出不同协议传输的结果 maomi yellow 十月 15, 2017 4:33:43 下午 io.netty.handler.logging.LoggingHandler channelRead 信息: [id: 0x930eab24, L:/0:0:0:0:0:0:0:0:8899] READ: [id: 0xf40f7b07, L:/127.0.0.1:8899 - R:/127.0.0.1:57879] 十月 15, 2017 4:33:43 下午 io.netty.handler.logging.LoggingHandler channelReadComplete 信息: [id: 0x930eab24, L:/0:0:0:0:0:0:0:0:8899] READ COMPLETE wangcai 2 十月 15, 2017 4:33:48 下午 io.netty.handler.logging.LoggingHandler channelRead 信息: [id: 0x930eab24, L:/0:0:0:0:0:0:0:0:8899] READ: [id: 0x3384f158, L:/127.0.0.1:8899 - R:/127.0.0.1:57914] 十月 15, 2017 4:33:48 下午 io.netty.handler.logging.LoggingHandler channelReadComplete 信息: [id: 0x930eab24, L:/0:0:0:0:0:0:0:0:8899] READ COMPLETE zhihao【深入浅出Netty通讯协议支持解析:第9篇教程】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
原来网ty可以支持好多种协议啊!这也太牛了吧!
有20位网友表示赞同!
我一直以为netty只能支持TCP协议,看来我需要好好学习一下它的其他协议支持。
有10位网友表示赞同!
知道netty支持多种协议能扩展应用范围,很高兴看到这份笔记分享!
有11位网友表示赞同!
想清楚netty如何实现对不同协议的支持,这篇文章一定要看一遍。
有12位网友表示赞同!
学习netty是为了做分布式系统开发吧?多种协议支持确实重要。
有17位网友表示赞同!
看了标题好奇这种协议支持具体都是哪些,笔记内容应该会介绍详细啊!
有17位网友表示赞同!
多了解一些netty的特性,感觉未来的项目开发更容易了。
有6位网友表示赞同!
学习netty好久了,感觉对协议支持这一块还不太了解,这篇笔记正好可以填补知识空缺。
有9位网友表示赞同!
之前一直使用其他的框架,现在想尝试netty,这方面多协议支持真的很吸引人。
有7位网友表示赞同!
网络通讯协议那么多,真是头大!希望笔记能介绍一些常见的协议和使用方法。
有8位网友表示赞同!
这篇笔记真棒,可以帮助我更好地理解netty的强大之处。
有8位网友表示赞同!
在实际开发中,选择合适的协议非常重要,这篇文章应该会提供很多参考信息。
有12位网友表示赞同!
netty真的越来越厉害了!支持多种协议意味着更加灵活的应用场景。
有7位网友表示赞同!
想了解更多关于netty和不同协议的应用案例,看一看这篇笔记也许会有答案。
有5位网友表示赞同!
分享这份笔记真是太棒了,可以帮助很多新手更快地入门netty。
有18位网友表示赞同!
对高性能、多协议通信感兴趣的人,这篇文章一定不能错过!
有17位网友表示赞同!
希望笔记能提供一些详细的代码示例,方便我更好地理解和学习。
有20位网友表示赞同!
netty的多协议支持真是个大亮点,可以帮助我们开发更强大的应用。
有20位网友表示赞同!
期待这篇笔记能深入解析netty在不同协议上的优缺点,以便我做出更好的选择。
有9位网友表示赞同!