# Java N(A)IO - Netty

> 原文链接：[Java N(A)IO - Netty](https://www.pdai.tech/md/java/io/java-io-nio-netty.html)

> Netty是一个高性能、异步事件驱动的NIO框架，提供了对TCP、UDP和文件传输的支持。作为当前最流行的NIO框架，Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用，一些业界著名的开源组件也基于Netty构建，比如RPC框架、zookeeper等。@pdai

## 1. NIO框架

目前流行的NIO框架非常的多。在论坛上、互联网上大家讨论和使用最多的有以下几种:

* 原生JAVA NIO框架:

  JAVA NIO通信框架基于多路复用IO原理，我们将详细讲解它的工作原理。
* APACHE MINA 2:

  是一个网络应用程序框架，用来帮助用户简单地开发高性能和高可扩展性的网络应用程序。它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的事件驱动的异步API。
* NETTY 4/5:

  Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具，用以快速开发高性能、高可靠性的网络服务器和客户端程序。我们将讲解NETTY 4 的工作原理。另外说一句: MINA和NETTY的主要作者是同一人Trustin Lee。
* Grizzly:

  Grizzly是一种应用程序框架，专门解决编写成千上万用户访问服务器时候产生的各种问题。使用JAVA NIO作为基础，并隐藏其编程的复杂性。

## 2. 比较好的基于NIO的开源框架(Netty)

### 2.1 优点

* api简单，开发门槛低
* 功能强大，内置了多种编码、解码功能
* 与其它业界主流的NIO框架对比，netty的综合性能最优
* 社区活跃，使用广泛，经历过很多商业应用项目的考验
* 定制能力强，可以对框架进行灵活的扩展

### 2.2 例子

```xml
<dependency>
     <groupId>org.jboss.netty</groupId>
     <artifactId>netty</artifactId>
     <version>3.2.5.Final</version>
</dependency>
```

* 服务端。接收客户端请求并将内容打印出来，同时发送一个消息收到回执。

  ```java
  public class NettyServer {

      private static int HEADER_LENGTH = 4;

      public void bind(int port) throws Exception {

          ServerBootstrap b = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
                                                                                    Executors.newCachedThreadPool()));

          // 构造对应的pipeline
          b.setPipelineFactory(new ChannelPipelineFactory() {

              public ChannelPipeline getPipeline() throws Exception {
                  ChannelPipeline pipelines = Channels.pipeline();
                  pipelines.addLast(MessageHandler.class.getName(), new MessageHandler());
                  return pipelines;
              }
          });
          // 监听端口号
          b.bind(new InetSocketAddress(port));
      }

      // 处理消息
      static class MessageHandler extends SimpleChannelHandler {

          public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
              // 接收客户端请求
              ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
              String message = new String(buffer.readBytes(buffer.readableBytes()).array(), "UTF-8");
              System.out.println("<服务端>收到内容=" + message);

              // 给客户端发送回执
              byte[] body = "服务端已收到".getBytes();
              byte[] header = ByteBuffer.allocate(HEADER_LENGTH).order(ByteOrder.BIG_ENDIAN).putInt(body.length).array();
              Channels.write(ctx.getChannel(), ChannelBuffers.wrappedBuffer(header, body));
              System.out.println("<服务端>发送回执,time=" + System.currentTimeMillis());

          }
      }

      public static void main(String[] args) {
          try {
              new NettyServer().bind(1088);
          } catch (Exception e) {
              e.printStackTrace();
          }
          ;
      }
  }
  ```
* 客户端。向服务端发送一个请求，然后打印服务端响应的内容。

  ```java
  public class NettyClient {

      private final ByteBuffer readHeader  = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
      private final ByteBuffer writeHeader = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
      private SocketChannel    channel;

      public void sendMessage(byte[] body) throws Exception {
          // 创建客户端通道
          channel = SocketChannel.open();
          channel.socket().setSoTimeout(60000);
          channel.connect(new InetSocketAddress(AddressUtils.getHostIp(), 1088));

          // 客户端发请求
          writeWithHeader(channel, body);

          // 接收服务端响应的信息
          readHeader.clear();
          read(channel, readHeader);
          int bodyLen = readHeader.getInt(0);
          ByteBuffer bodyBuf = ByteBuffer.allocate(bodyLen).order(ByteOrder.BIG_ENDIAN);
          read(channel, bodyBuf);
          System.out.println("<客户端>收到响应内容: " + new String(bodyBuf.array(), "UTF-8") + ",长度:" + bodyLen);
      }

      private void writeWithHeader(SocketChannel channel, byte[] body) throws IOException {
          writeHeader.clear();
          writeHeader.putInt(body.length);
          writeHeader.flip();
          // channel.write(writeHeader);
          channel.write(ByteBuffer.wrap(body));
      }

      private void read(SocketChannel channel, ByteBuffer buffer) throws IOException {
          while (buffer.hasRemaining()) {
              int r = channel.read(buffer);
              if (r == -1) {
                  throw new IOException("end of stream when reading header");
              }
          }
      }

      public static void main(String[] args) {
          String body = "客户发的测试请求！";
          try {
              new NettyClient().sendMessage(body.getBytes());
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
  }
  ```

## 2. 参考文章

* <https://blog.csdn.net/yinwenjie/article/details/48829419>
* <https://blog.csdn.net/yinwenjie/article/details/48969853>
* [Netty入门简介](https://mp.weixin.qq.com/s/RPTETiULRAkOS-ZTd6xM2A)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ldbmcs.gitbook.io/java/java-2/java-io-and-nio-and-aio/java-io-netty.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
