Netty实现按字节解析的socket协议

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               
Netty内部实现了很多通用协议的编码和解码。如果要实现自定义的协议,则需要自己实现编码或解码的功能。 继承ChannelInboundHandlerAdapter类,就可以实现一个自定义的解码器。但如果发送比较长的内容,则会出现内容读取不完整的问题。其实比较简单的一个实现,就是设定协议头的几个字节为消息的长度即可,并在发送消息和处理消息时,处理消息的长度即可。Server端的代码如下:TcpServer.javaimport org.apache.log4j.Logger;  import org.apache.log4j.PropertyConfigurator;  import io.netty.bootstrap.ServerBootstrap;  import io.netty.channel.ChannelInitializer;  import io.netty.channel.ChannelOption;import io.netty.channel.ChannelPipeline;  import io.netty.channel.EventLoopGroup;  import io.netty.channel.nio.NioEventLoopGroup;  import io.netty.channel.socket.SocketChannel;  import io.netty.channel.socket.nio.NioServerSocketChannel;  import io.netty.handler.codec.LengthFieldBasedFrameDecoder;  import io.netty.handler.codec.LengthFieldPrepender;  import io.netty.handler.codec.bytes.ByteArrayDecoder;import io.netty.handler.codec.bytes.ByteArrayEncoder;import io.netty.handler.codec.protobuf.ProtobufDecoder;import io.netty.handler.codec.serialization.ClassResolvers;import io.netty.handler.codec.serialization.ObjectDecoder;import io.netty.handler.codec.serialization.ObjectEncoder;import io.netty.handler.codec.string.StringDecoder;  import io.netty.handler.codec.string.StringEncoder;  import io.netty.util.CharsetUtil;    public class TcpServer {        private static final Logger logger = Logger.getLogger(TcpServer.class);      private static final String IP = "127.0.0.1";      private static final int PORT = 9999;           protected static final int BIZGROUPSIZE = Runtime.getRuntime().availableProcessors()*2; //默认           protected static final int BIZTHREADSIZE = 4;               private static final EventLoopGroup bossGroup = new NioEventLoopGroup(BIZGROUPSIZE);      private static final EventLoopGroup workerGroup = new NioEventLoopGroup(BIZTHREADSIZE);            protected static void run() throws Exception {          ServerBootstrap b = new ServerBootstrap();          b.group(bossGroup, workerGroup);          b.channel(NioServerSocketChannel.class);                  b.childHandler(new ChannelInitializer() {              @Override              public void initChannel(SocketChannel ch) throws Exception {                  ChannelPipeline pipeline = ch.pipeline();                  pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));                  pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));  //                pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));  //                pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));                pipeline.addLast(new SocketByteHandler());              }          });                 b.bind(IP, PORT).sync();          logger.info("TCP服务器已启动");      }            protected static void shutdown() {          workerGroup.shutdownGracefully();          bossGroup.shutdownGracefully();      }        public static void main(String[] args) throws Exception {            PropertyConfigurator.configure("log/log4j.properties");                    logger.info("开始启动TCP服务器...");          TcpServer.run();  //      TcpServer.shutdown();      }  }  LengthFieldBasedFrameDecoder和LengthFieldPrepender就是设定协议头长度的,我这里设定协议头的长度为4个字节。协议处理类:SocketByteHandler.javaimport java.nio.ByteBuffer;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;public class SocketByteHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg)throws Exception {// super.channelRead(ctx, msg);ByteBuf result = (ByteBuf) msg;byte[] result1 = new byte[result.readableBytes()];          // msg中存储的是ByteBuf类型的数据,把数据读取到byte[]中          result.readBytes(result1);          String resultStr = new String(result1);        System.out.println("Client said:" + resultStr);          // 释放资源,这行很关键          result.release();          String response = "I am ok!";          // 在当前场景下,发送的数据必须转换成ByteBuf数组          ByteBuf encoded = ctx.alloc().buffer(4 * response.length());          encoded.writeBytes(response.getBytes());          ctx.write(encoded);          ctx.flush(); }@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {// TODO Auto-generated method stubsuper.channelReadComplete(ctx);ctx.flush();}}客户端可以使用最简单的socket来实现即可,如:public static void sendMsgBySocket(byte[] msg){    try {Socket socket = new Socket();socket.connect(new InetSocketAddress(HOST,PORT));socket.setKeepAlive(true);OutputStream out = socket.getOutputStream();ByteBuffer header = ByteBuffer.allocate(4);header.putInt(msg.length);out.write(header.array());out.write(msg);out.flush();InputStream in = socket.getInputStream();byte[] buff = new byte[4096];int readed = in.read(buff);if(readed > 0){String str = new String(buff,4,readed);logger.info("client received msg from server:"+str);}out.close();} catch (UnknownHostException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}       }可以看到,header中就是给出的要发送的消息内容的总长度,但不包含协议头。这样,server端收到后,会自动忽略掉协议头的内容,这就是Netty的好处。客户端如果读取server端的返回内容,而且用基本的socket(非Netty),则需要自己处理协议头:String str = new String(buff,4,readed);跳过协议头的4个字节长度后,就是Server端返回的真正的内容。

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述

猜你喜欢

转载自blog.csdn.net/yrryyff/article/details/84195390