说明
Netty 封装了 Java NIO 的很多功能,大大简化了 Java 网络编程的难度,同时 Netty 也支持多种协议,Netty 架构图如下
注:上图来自 Netty 官网
BIO 模型
传统的Java BIO模型代码如下
客户端代码
import java.net.Socket; import java.util.Date; /** * 传统 BIO 的客户端实现 * * @author <a href="mailto:410486047@qq.com">Grey</a> * @date 2022/9/12 * @since 1.1 */ public class IOClient { public static void main(String[] args) { new Thread(() -> { try { Socket socket = new Socket("127.0.0.1", 8000); while (true) { try { socket.getOutputStream().write((new Date() + ": hello world").getBytes()); Thread.sleep(2000); } catch (Exception e) { } } } catch (Exception e) { } }).start(); } }
服务端代码
package bio; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; /** * 传统 BIO 的 服务端实现 * * @author <a href="mailto:410486047@qq.com">Grey</a> * @date 2022/9/12 * @since 1.1 */ public class IOServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8000); new Thread(() -> { while (true) { try { // 阻塞 Socket socket = serverSocket.accept(); new Thread(() -> { try { int len; byte[] data = new byte[1024]; InputStream inputStream = socket.getInputStream(); // 按照字节流的方式读取数据 while ((len = inputStream.read(data)) != -1) { System.out.println(new String(data, 0, len)); } } catch (IOException e) { } }).start(); } catch (IOException e) { } } }).start(); } }
上述代码比较直白,缺点也很明显
每个连接创建成功后都需要由一个线程来维护,同一时刻有大量线程处于阻塞状态,此外,线程数量太多,也会导致操作系统频繁进行线程切换,使得应用性能下降。
NIO 模型
为了解决 BIO 的问题,引入了 NIO,即:一个新的连接来了以后,不会创建一个while 死循环取监听有数据可读,而是直接把这条连接注册到 Selector 上。然后,通过检查这个 Selector,就可以批量监测出有数据可读的连接,进而读取数据。
BIO读写是面向流的,一次性只能从流中读取一个字节或者多字节,并且读完之后流无法再读取,需要自己缓存数据。而 NIO 的读写是面向 Buffer 的,可以随意读取里面任何字节的数据,不需要自己缓存数据,只需要移动读写指针即可。
但是 Java 原生的 NIO 代码编程非常繁琐,一个简单的服务端代码,使用 NIO 模型,代码如下
package nio; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selecto