分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > IT知识

Netty 5 ?自定义协议 ?教程

发布时间:2023-09-06 01:06责任编辑:白小东关键词:暂无标签


网上好多连接好多demo都不是netty5的,都是以前的版本,况且还有好多运行时老报错。入门级demo就不写了,估计都是那些老套路。好多公司都会有最佳实践,今天就说说如何自定义协议,一般自定义协议都是公司内部各个部门定义的,当然了我写的比较简单。


注意:

本教程是采用netty-all-5.0.0.Alpha2.jar,netty5的版本,不是网上很多的例子都是采用以前老大版本。


自定义协议:

协议 {

协议头(header)

消息体(body)

}


header格式 {

固定头,

命令码,

版本号,

长度

}


自定义协议嘛,都是可以自己定义实现的,内部商量好就可以。

packagecom.nio.netty;importjava.io.Serializable;importjava.util.Arrays;/***Createdbysdcon2017/8/26.*/publicclassMsgHeaderimplementsSerializable{//固定头privatebytestartTag;//命令码,4位privatebyte[]cmdCode;//版本2位privatebyte[]version;privateintlength;publicbyte[]getVersion(){returnversion;}publicvoidsetVersion(byte[]version){this.version=version;}publicbyte[]getCmdCode(){returncmdCode;}publicvoidsetCmdCode(byte[]cmdCode){this.cmdCode=cmdCode;}publicbytegetStartTag(){returnstartTag;}publicvoidsetStartTag(bytestartTag){this.startTag=startTag;}publicintgetLength(){returnlength;}publicvoidsetLength(intlength){this.length=length;}@OverridepublicStringtoString(){return"MsgHeader{"+"startTag="+startTag+",cmdCode="+Arrays.toString(cmdCode)+",version="+Arrays.toString(version)+",length="+length+‘}‘;}}


packagecom.nio.netty;/***Createdbysdcon2017/8/26.*/publicclassMessage{privateMsgHeaderheader;privateObjectbody;//检验和//privatebytecrcCode;//publicbytegetCrcCode(){//returncrcCode;//}////publicvoidsetCrcCode(bytecrcCode){//this.crcCode=crcCode;//}publicMsgHeadergetHeader(){returnheader;}publicvoidsetHeader(MsgHeaderheader){this.header=header;}publicObjectgetBody(){returnbody;}publicvoidsetBody(Objectbody){this.body=body;}@OverridepublicStringtoString(){return"Message{"+"header="+header+",body="+body+//",crcCode="+crcCode+‘}‘;}}


消息格式定义完成后,需要编码和解码,这里采用ByteToMessageDecoder MessageToByteEncoder,这两个编解码。

packagecom.nio.netty;importio.netty.buffer.ByteBuf;importio.netty.channel.ChannelHandlerContext;importio.netty.handler.codec.MessageToByteEncoder;/***Createdbysdcon2017/8/26.*/publicclassMsgEncoderextendsMessageToByteEncoder{publicstaticbytegetIndexToByte(inti,intindex){if(index==0){return(byte)(i%10);}else{intnum=(int)Math.pow(10,index);return(byte)((i/num)%10);}}@Overrideprotectedvoidencode(ChannelHandlerContextchannelHandlerContext,Objecto,ByteBufout)throwsException{if(oinstanceofMessage){try{Messagemsg=(Message)o;if(msg==null||msg.getHeader()==null){thrownewException("Theencodemessageisnull");}out.writeByte(msg.getHeader().getStartTag());out.writeBytes(msg.getHeader().getCmdCode());//占位byte[]lengthBytes=newbyte[]{0,0,0,0};out.writeBytes(lengthBytes);out.writeBytes(msg.getHeader().getVersion());Stringbody=(String)msg.getBody();intlength=0;if(body!=null){byte[]bodyBytes=body.getBytes();out.writeBytes(bodyBytes);length=bodyBytes.length;//if(Constants.CRCCODE_DEFAULT!=msg.getCrcCode()){//msg.setCrcCode(CRC8.calcCrc8(bodyBytes));//}//msg.setCrcCode();}//长度从int转换为byte[4]bytel1=getIndexToByte(length,3);bytel2=getIndexToByte(length,2);bytel3=getIndexToByte(length,1);bytel4=getIndexToByte(length,0);lengthBytes=newbyte[]{l1,l2,l3,l4};out.setBytes(5,lengthBytes);System.out.println("encoder:"+msg.getBody());//out.writeByte(msg.getCrcCode());}catch(Exceptione){e.printStackTrace();throwe;}}}}
packagecom.nio.netty;importio.netty.buffer.ByteBuf;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelPromise;importio.netty.handler.codec.ByteToMessageCodec;importio.netty.handler.codec.ByteToMessageDecoder;importjava.util.List;/***Createdbysdcon2017/8/26.*/publicclassMsgDecoderextendsByteToMessageDecoder{@Overrideprotectedvoiddecode(ChannelHandlerContextctx,ByteBufin,Listout)throwsException{try{//内部协议这块可以约定一下,超过多大的长度就不可以了。//if(in.readableBytes()<12){//return;//}System.out.println("开始解码消息,消息长度:"+in.readableBytes());in.markReaderIndex();//设置一些消息的属性Messagemessage=newMessage();MsgHeaderheader=newMsgHeader();header.setStartTag(in.readByte());byte[]cmdCode=newbyte[4];in.readBytes(cmdCode);header.setCmdCode(cmdCode);System.out.println(newString(cmdCode,"UTF-8"));//长度从byte[4]转intbyte[]lengthBytes=newbyte[4];in.readBytes(lengthBytes);intlength=toInt(lengthBytes);System.out.println("header:"+length);header.setLength(length);if(length<0||length>10240){//过长消息或不合法消息thrownewIllegalArgumentException("wrongmessagelength");}byte[]version=newbyte[2];in.readBytes(version);header.setVersion(version);System.out.println("version:"+newString(version,"UTF-8"));if(header.getLength()>0){System.out.println("bytebuffer可读的范围"+in.readableBytes());if(in.readableBytes()>length+1){in.resetReaderIndex();System.out.println("返回了");return;}//读取body里的内容byte[]bodyBytes=newbyte[header.getLength()];in.readBytes(bodyBytes);message.setBody(newString(bodyBytes,"UTF-8"));}//crccode暂时去掉//message.setCrcCode(in.readByte());//设置头部message.setHeader(header);System.out.println("body:"+message.getBody());out.add(message);}catch(Exceptione){e.printStackTrace();throwe;}}publicstaticinttoInt(byte[]bytes){intvalue=0;for(inti=0;i<bytes.length;i++){intnum=(int)Math.pow(10,bytes.length-1-i);value+=num*bytes[i];}returnvalue;}}


消息的类已经处理完了,下面来开始写客户端和服务端的代码。

客户端代码:

packagecom.nio.netty;importio.netty.bootstrap.Bootstrap;importio.netty.buffer.ByteBuf;importio.netty.buffer.ByteBufUtil;importio.netty.buffer.Unpooled;importio.netty.channel.*;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioSocketChannel;/***Createdbysdcon2017/8/26.*/publicclassMsgClient{publicvoidconnect(Stringip,intport)throwsException{EventLoopGroupworkerGroup=newNioEventLoopGroup();Messagemessage=newMessage();StringmsgStr="我想发送一条消息";MsgHeaderheader=newMsgHeader();header.setStartTag(newByte("0"));header.setCmdCode("1234".getBytes());header.setLength(msgStr.length());header.setVersion("11".getBytes());message.setBody(msgStr);message.setHeader(header);try{Bootstrapbs=newBootstrap();bs.group(workerGroup).channel(NioSocketChannel.class).handler(newChildChannelHandler(message));ChannelFuturef=bs.connect(ip,port).sync();//写入消息f.channel().writeAndFlush(message).sync();f.channel().closeFuture().sync();}finally{workerGroup.shutdownGracefully();}}publicstaticclassChildChannelHandlerextendsChannelInitializer{Messagemessage;publicChildChannelHandler(Messagemessage){this.message=message;}@OverrideprotectedvoidinitChannel(Channelchannel)throwsException{channel.pipeline().addLast(newMsgDecoder()).addLast(newMsgEncoder());}}publicstaticvoidmain(String[]args){try{newMsgClient().connect("127.0.0.1",9080);}catch(Exceptione){e.printStackTrace();}}}


服务端代码

packagecom.nio.netty;importio.netty.bootstrap.ServerBootstrap;importio.netty.channel.*;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioServerSocketChannel;/***Createdbysdcon2017/8/26.*/publicclassMsgServer{publicvoidbind(intport)throwsException{EventLoopGroupbossGroup=newNioEventLoopGroup();EventLoopGroupworkerGroup=newNioEventLoopGroup();try{ServerBootstrapsb=newServerBootstrap();sb.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,1024).childHandler(newChildChannelHandler());ChannelFuturecf=sb.bind(port).sync();cf.channel().closeFuture().sync();}finally{bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}publicstaticclassChildChannelHandlerextendsChannelInitializer{@OverrideprotectedvoidinitChannel(Channelchannel)throwsException{channel.pipeline().addLast(newMsgDecoder()).addLast(newMsgEncoder());}}publicstaticvoidmain(String[]args){try{newMsgServer().bind(9080);}catch(Exceptione){e.printStackTrace();}}}


运行的时候先运行服务端,否则胡报如下的错误,

-all-5.0.0.Alpha2.jarcom.nio.netty.MsgClientjava.net.ConnectException:Connectionrefused:nofurtherinformation:/127.0.0.1:9080atsun.nio.ch.SocketChannelImpl.checkConnect(NativeMethod)atsun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:716)atio.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:223)atio.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:276)atio.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:531)atio.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:471)atio.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:385)atio.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:351)atio.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)atio.netty.util.internal.chmv8.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1412)atio.netty.util.internal.chmv8.ForkJoinTask.doExec(ForkJoinTask.java:280)atio.netty.util.internal.chmv8.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:877)atio.netty.util.internal.chmv8.ForkJoinPool.scan(ForkJoinPool.java:1706)atio.netty.util.internal.chmv8.ForkJoinPool.runWorker(ForkJoinPool.java:1661)atio.netty.util.internal.chmv8.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:126)Processfinishedwithexitcode0


成功会打印以下信息:

开始解码消息,消息长度:35开始解码消息,消息长度2222:341234header:24version:11可读的范围24body:我想发送一条消息Processfinishedwithexitcode1


基本流程已经走完,学框架,我还是习惯先写例子,然后再学概念性的东西,会理解的更深刻一些。


参考了这篇文章,但是这篇文章似乎看不了结果,所以还是自己改了改,写了点东西。

http://www.blogs8.cn/posts/Wx8Sd43


本文出自 “不积跬步无以至千里” 博客,请务必保留此出处http://shangdc.blog.51cto.com/10093778/1959680

Netty 5 ?自定义协议 ?教程

原文地址:http://shangdc.blog.51cto.com/10093778/1959680

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved