1. 什么是Netty
    Netty 是一个基于NIO的客户端、服务器端的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性
  2. Netty应用场景
    分布式开源框架中dubbo、Zookeeper,RocketMQ底层rpc通讯使用就是netty。
    游戏开发中,底层使用netty通讯。
  3. 为什么选择netty
     解决了NIO代码的复杂问题,容错机制问题
  • netty---服务端
  1. 创建maven工程,在pom.xml中引入依赖---开发中尽量用netty4.0以上版本
     
    <dependency>
          <groupId>io.netty</groupId>
          <artifactId>netty</artifactId>
          <version>3.3.0.Final</version>
        </dependency>
  2. 创建NettyServer.java
     
    import org.jboss.netty.bootstrap.ServerBootstrap;
    import org.jboss.netty.channel.*;
    import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
    import org.jboss.netty.handler.codec.string.StringDecoder;
    import org.jboss.netty.handler.codec.string.StringEncoder;
    import java.net.InetSocketAddress;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * netty服务端
     * Create by wangxb
     * 2019-07-27 19:06
     */
    // 监听类
    class ServerHanlder extends SimpleChannelHandler {
        // 通道被关闭的时候会触发
        @Override
        public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            super.channelClosed(ctx, e);
            System.out.println("channelClosed......................");
        }
    
        // 必须要建立连接,关闭通道的时候才会触发
        @Override
        public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            super.channelDisconnected(ctx, e);
            System.out.println("channelDisconnected.......................");
        }
    
        // 接受出现异常
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
            super.exceptionCaught(ctx, e);
            System.out.println("exceptionCaught..............................");
        }
    
        // 接受客户端数据..
        @Override
        public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
            super.messageReceived(ctx, e);
            System.out.println("--------------------接受客户端数据--------------------");
            System.out.println(e.getMessage());
            System.out.println("-------------------------end---------------------------");
            ctx.getChannel().write("客户端, 你好啊!");
        }
    }
    
    // netty服务类
    public class NettyServer {
    
    
        public static void main(String[] args) {
            // 1.创建服务对象
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            // 2.创建两个线程池 第一个 监听端口号 nio监听
            ExecutorService boos = Executors.newCachedThreadPool();
            ExecutorService wook = Executors.newCachedThreadPool();
            // 3.将线程池放入工程
            serverBootstrap.setFactory(new NioServerSocketChannelFactory(boos, wook));
            // 4.设置管道工程
            serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
                // 设置管道
                public ChannelPipeline getPipeline() throws Exception {
                    ChannelPipeline pipeline = org.jboss.netty.channel.Channels.pipeline();
                    // 传输数据的时候直接为string类型
                    pipeline.addLast("decoder", new StringDecoder());
                    pipeline.addLast("encoder", new StringEncoder());
                    // 设置事件监听类
                    pipeline.addLast("serverHanlder", new ServerHanlder());
                    return pipeline;
    
                }
            });
            // 绑定端口号
            serverBootstrap.bind(new InetSocketAddress(8080));
            System.out.println("服务器端已经被启动.....");
    //		while (true) {
    //			try {
    //				Thread.sleep(500);
    //			} catch (Exception e) {
    //				// TODO: handle exception
    //			}
    //			System.out.println("说明netty是异步的.....");
    //
    //		}
        }
    }
  3. 测试:启动NettyServer.java的main方法
    浏览器访问地址:  http://127.0.0.1:8080/
     
  • netty---客户端
  1. 创建NettyClient.java
     
    import org.jboss.netty.bootstrap.ClientBootstrap;
    import org.jboss.netty.channel.*;
    import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
    import org.jboss.netty.handler.codec.string.StringDecoder;
    import org.jboss.netty.handler.codec.string.StringEncoder;
    import java.net.InetSocketAddress;
    import java.util.Scanner;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * netty客户端
     * Create by wangxb
     * 2019-07-27 19:52
     */
    // 监听类
    class ClientHanlder extends SimpleChannelHandler{
    
        // 通道被关闭的时候会触发
        @Override
        public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            super.channelClosed(ctx, e);
            System.out.println("channelClosed.......................");
        }
    
        // 必须要建立连接,关闭通道的时候才会触发
        @Override
        public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            super.channelDisconnected(ctx, e);
            System.out.println("channelDisconnected.....................");
        }
    
        // 接受出现异常
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
            super.exceptionCaught(ctx, e);
            System.out.println("exceptionCaught........................");
        }
    
        // 接受客户端数据..
        @Override
        public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
            super.messageReceived(ctx, e);
            System.out.println("服务器向客户端回复的内容------------------------------------");
            System.out.println( e.getMessage());
        }
    }
    
    public class NettyClient {
    
        public static void main(String[] args) {
            // 1.创建服务对象
            ClientBootstrap clientBootstrap = new ClientBootstrap();
            // 2.创建两个线程池 第一个 监听端口号 nio监听
            ExecutorService boos = Executors.newCachedThreadPool();
            ExecutorService wook = Executors.newCachedThreadPool();
            // 3.将线程池放入工程
            clientBootstrap.setFactory(new NioClientSocketChannelFactory(boos, wook));
            // 4.设置管道工程
            clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
                // 设置管道
                public ChannelPipeline getPipeline() throws Exception {
                    ChannelPipeline pipeline = org.jboss.netty.channel.Channels.pipeline();
                    // 传输数据的时候直接为string类型
                    pipeline.addLast("decoder", new StringDecoder());
                    pipeline.addLast("encoder", new StringEncoder());
                    // 设置事件监听类
                    pipeline.addLast("clientHanlder", new ClientHanlder());
                    return pipeline;
    
                }
            });
            // 绑定端口号
            ChannelFuture connect = clientBootstrap.connect(new InetSocketAddress("127.0.0.1", 8080));
            Channel channel = connect.getChannel();
            System.out.println("client start");
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.println("请输入内容:");
                channel.write(scanner.next());
    
            }
        }
    }
  2. 测试   启动服务端,启动客户端
     
  • TCP粘包、拆包
  1. 粘包与拆包的概念 
    粘包:  将多个数据包封装成一个数据包。比如客户端发送了2条消息: a、b,服务端直接当成了一条消息读取:  ab   
    拆包:  将一个数据包拆成了多个数据包。比如客户端发送了1条消息: hello,服务端当成了多条消息: h、e、l、l、0。  
        
  2. 产生的原因 
     
      
    如图所示:  客户端发送数据到服务端,是先到缓冲区,服务端然后直接从缓冲区读取。
      
    粘包产生原因:   客户端发送的多条数据小于缓存区大小,则服务端直接分1次从缓存区读取。
    拆包产生原因:   客户端发送的数据大于缓存区的大小,则服务端直接分多次从缓存区读取。

    还有其他的网络延迟等原因..... 
        
  3. 解决办法:
    1). 以固定的消息长度发送数据,到缓冲区
    2). 可以在数据之间设置一些特殊字符(\n或者\r\n)
    3). 利用编码器LineBaseDFrameDecoder解决tcp粘包的问题

    sc.pipeline().addLast(new FixedLengthFrameDecoder(10));

  • 序列化与反序列化   
        
  1. 产生原因   
        
           因为TCP/IP协议只支持字节数组的传输,不能直接传对象。所以网络传输中,客户端需要将对象转成字节数组进行发送,服务端再将接收的字节数组转成对象。主要用于网络传输、数据持久化等。
           
  2. 序列化与反序列化概念  
        
    序列化:  
    就是将对象转换为字节数组一般也将序列化称为编码(Encode);  
         
    反序列化:  
    就是将网络、磁盘中接收的字节数组还原成对象,一般也将反序列化称为解码(Decode); 
        
  3. 实现序列化的要求  
    只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常!
     
  4. 序列化ID的作用
           在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与对象中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。 
  5. 几种常见序列化协议 
        
    a)   JSON    客户端将对象转成json类型在网络传输,服务端获取json通过key和value再将其转为对象。可实现跨 语言
    b)   XML 
    c)   Fastjson
    d)   Thrift..... 
    e)   MessagePack    
     
     
      
        
     
     
      
        
     
     
     
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐