Netty - 基本使用介绍

文章目录

  • Netty - 基本使用介绍
        • 1.什么是Netty?
        • 2.使用Netty的好处
        • 3.Netty基本使用
            • pom.xml
            • ServerHandlerOne.java
            • NettyServer.java
            • ClientHandlerOne.java
            • NettyClient.java
            • NettyClient运行结果:
            • NettyServer运行结果:

Netty - 基本使用介绍

1.什么是Netty?

 Netty是JBoss提供的高效的Java NIO开发框架。基于Java NIO client-server的网络应用框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来开发网络应用程序,这种新的方式使它很容易使用和具有很强的扩展性。
Netty的内部实现是很复杂的,但是Netty提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,采用了多路复用处理方式。

2.使用Netty的好处

 Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络服务器和客户端程序。换句话说,Netty是一个NIO框架,使用它可以简单快速地开发网络应用程序,比如客户端和服务端的协议。

 Netty大大简化了网络程序的开发过程比如TCP和UDP的 Socket的开发。“快速和简单”并不意味着应用程序会有难维护和性能低的问题,Netty是一个精心设计的框架,它从许多协议的实现中吸收了很多的经验比如FTP、SMTP、HTTP、许多二进制和基于文本的传统协议,Netty在不降低开发效率、性能、稳定性、灵活性情况下,成功地找到了解决方案。

 Netty目前使用的版本大致分为netty3.x 和 netty4.x、netty5.x。Hadoop、Dubbo、Akka等具有分布式功能的框架,底层RPC通信都是基于netty实现的,这些框架使用的版本通常都还在用netty3.x。Netty在游戏开发公司也应用广泛,最新的游戏服务器有部分公司可能已经开始采用netty4.x 或 netty5.x。

3.Netty基本使用

 之前博客在介绍NIO以及Netty简述的时候也做了简单使用介绍,这里我们在真正使用Netty前,我们先来理解几个相关概念。

  • Buffer
    • org.jboss.netty.buffer包的接口及类,该包核心的接口是ChannelBuffer和ChannelBufferFactory,Netty使用ChannelBuffer来存储并操作读写的网络数据。ChannelBuffer除了提供和ByteBuffer类似的方法,还提供了 一些实用方法,具体可参考其API文档。
  • Channel
    • Channel主要提供的功能如下
      • 1)当前Channel的状态信息,比如是打开还是关闭等。
      • 2)通过ChannelConfig可以得到的Channel配置信息。
      • 3)Channel所支持的如read、write、bind、connect等IO操作。
      • 4)得到处理该Channel的ChannelPipeline,既而可以调用其做和请求相关的IO操作。
      • 在Channel实现方面,以通常使用的nio socket来说,Netty中的NioServerSocketChannel和NioSocketChannel分别封装了java.nio中包含的 ServerSocketChannel和SocketChannel的功能。
  • ChannelEvent
    • Netty是事件驱动的,其通过ChannelEvent来确定事件流的方向。一个ChannelEvent是依附于Channel的 ChannelPipeline来处理,并由ChannelPipeline调用ChannelHandler来做具体的处理。
  • ChannelPipeline
    • Netty 在事件处理上,是通过ChannelPipeline来控制事件流,通过调用注册其上的一系列ChannelHandler来处理事件,这也是典型的拦截器模式。
  • NioEventLoopGroup
    • 是用来处理I/O操作的多线程事件循环器,Netty提供了许多不同的EventLoopGroup的实现用来处理不同传输协议。在之前我们实现了一个服务端的应用,因此会有2个NioEventLoopGroup会被使用。第一个通常成为boss线程,负责监听端口建立连接。第二个成为worker线程,负责处理已建立的连接数据读写,当boss线程接收到连接,就会把连接信息注册到worker上。如何确定多少个线程已经被使用,如何映射到已经创建的Channels上都需要依赖于EventLoopGroup的实现,并且可以通过构造函数来配置他们的关系。
pom.xml
 <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty</artifactId>
            <version>3.10.5.Final</version>
        </dependency>
ServerHandlerOne.java
package com.proto.server;

import org.jboss.netty.channel.*;

/**
 * 消息接受处理类
 * @author hzk
 * @date 2018/8/16
 */
public class ServerHandlerOne extends SimpleChannelHandler {

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        //接收数据
        //ChannelBuffer channelBuffer = (ChannelBuffer)e.getMessage();
        //System.out.println("Receive:"+new String(channelBuffer.array()));
        //decoder
        System.out.println((String)e.getMessage());

        //回写数据
        //encoder
        ctx.getChannel().write("ok!");
        super.messageReceived(ctx, e);
    }

    /**
     * 捕获异常
     * @param ctx
     * @param e
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        System.out.println("exceptionCaught");
        super.exceptionCaught(ctx, e);
    }

    /**
     * 新连接 通常用来检测IP是否是黑名单
     * @param ctx
     * @param e
     * @throws Exception
     */
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        System.out.println("channelConnected");
        super.channelConnected(ctx, e);
    }

    /**
     * 必须是连接已经建立,关闭通道的时候才会触发,可以在用户断线的时候清除用户的缓存数据等(只有在连接建立后断开才会调用)
     * @param ctx
     * @param e
     * @throws Exception
     */
    @Override
    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        System.out.println("channelDisconnected");
        super.channelDisconnected(ctx, e);
    }

    /**
     * channel关闭的时候触发(无论连接是否成功都会调用关闭资源)
     * @param ctx
     * @param e
     * @throws Exception
     */
    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        System.out.println("channelClosed");
        super.channelClosed(ctx, e);
    }
}


NettyServer.java
package com.proto.server;


import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
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服务端
 * @author hzk
 * @date 2018/8/16
 */
public class NettyServer {

    public static void main(String[] args){

        //服务类引导
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        //boss线程监听端口,worker线程负责数据读写
        ExecutorService boss = Executors.newCachedThreadPool();
        ExecutorService worker = Executors.newCachedThreadPool();

        //设置nio socket工厂
        serverBootstrap.setFactory(new NioServerSocketChannelFactory(boss,worker));

        //设置管道的工厂
        serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {

            @Override
            public ChannelPipeline getPipeline() throws Exception {
                ChannelPipeline pipeline = Channels.pipeline();
                //接收
                pipeline.addLast("decoder",new StringDecoder());
                //回写
                pipeline.addLast("encoder",new StringEncoder());
                pipeline.addLast("serverHandlerOne",new ServerHandlerOne());
                return pipeline;
            }
        });

        serverBootstrap.bind(new InetSocketAddress(8888));

        System.out.println("Netty server start...");
    }
}

ClientHandlerOne.java
package com.proto.client;

import org.jboss.netty.channel.*;

/**
 * 消息接受处理类
 * @author hzk
 * @date 2018/8/16
 */
public class ClientHandlerOne extends SimpleChannelHandler {

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        //接收数据
        //ChannelBuffer channelBuffer = (ChannelBuffer)e.getMessage();
        //System.out.println("Receive:"+new String(channelBuffer.array()));
        //decoder
        System.out.println((String)e.getMessage());

        super.messageReceived(ctx, e);
    }

    /**
     * 捕获异常
     * @param ctx
     * @param e
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        System.out.println("exceptionCaught");
        super.exceptionCaught(ctx, e);
    }

    /**
     * 新连接 通常用来检测IP是否是黑名单
     * @param ctx
     * @param e
     * @throws Exception
     */
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        System.out.println("channelConnected");
        super.channelConnected(ctx, e);
    }

    /**
     * 必须是连接已经建立,关闭通道的时候才会触发,可以在用户断线的时候清除用户的缓存数据等(只有在连接建立后断开才会调用)
     * @param ctx
     * @param e
     * @throws Exception
     */
    @Override
    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        System.out.println("channelDisconnected");
        super.channelDisconnected(ctx, e);
    }

    /**
     * channel关闭的时候触发(无论连接是否成功都会调用关闭资源)
     * @param ctx
     * @param e
     * @throws Exception
     */
    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        System.out.println("channelClosed");
        super.channelClosed(ctx, e);
    }
}


NettyClient.java
package com.proto.client;

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客户端
 * @author hzk
 * @date 2018/8/17
 */
public class NettyClient {
    
    public static void main(String[] args){
        //客户端引导
        ClientBootstrap clientBootstrap = new ClientBootstrap();

        ExecutorService boss = Executors.newCachedThreadPool();
        ExecutorService worker = Executors.newCachedThreadPool();

        //设置nio socket工厂
        clientBootstrap.setFactory(new NioClientSocketChannelFactory(boss,worker));

        //设置管道的工厂
        clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {

            @Override
            public ChannelPipeline getPipeline() throws Exception {
                ChannelPipeline pipeline = Channels.pipeline();
                pipeline.addLast("decoder", new StringDecoder());
                pipeline.addLast("encoder", new StringEncoder());
                pipeline.addLast("clientHandlerOne", new ClientHandlerOne());
                return pipeline;
            }
        });

        //连接服务端
        ChannelFuture connect = clientBootstrap.connect(new InetSocketAddress(8888));
        Channel channel = connect.getChannel();

        System.out.println("Client start...");
        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.println("Please input:");
            channel.write(scanner.next());
        }
    }
}

NettyClient运行结果:
Client start...
channelConnected
Please input:
send hello
ok!
NettyServer运行结果:
Netty server start...
channelConnected
sendhello
senhi
exceptionCaught
十月 30, 2018 6:49:02 下午 org.jboss.netty.channel.SimpleChannelHandler
警告: EXCEPTION, please implement com.proto.server.ServerHandlerOne.exceptionCaught() for proper handling.
java.io.IOException: 远程主机强迫关闭了一个现有的连接。
	at sun.nio.ch.SocketDispatcher.read0(Native Method)
	at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
	at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
	at sun.nio.ch.IOUtil.read(IOUtil.java:192)
	at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
	at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:64)
	at org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:108)
	at org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:337)
	at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:89)
	at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:178)
	at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
	at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

channelDisconnected
channelClosed

 Q:channelDisconnected与channelClosed的区别:
 channelDisconnected:只有在连接建立后断开才会调用
 channelClosed:无论连接是否成功都会调用关闭资源
 这里推荐几篇博客给大家一起学习理解
 Netty——基本使用介绍
 Netty 5用户指南
 Netty实现原理浅析

你可能感兴趣的:(Java,Socket,Netty)