Netty Java BIO 编程 (一)
1.IO模型,首先对Java网络编程的发展流程回顾一下,最开始的TCP/IP服务器,Java原生IO,到后面的BIO,最后是Netty。
2.Java 共支持 3 种网络编程模型/IO 模式: BIO、 NIO、 AIO 。
Java BIO : 同步并阻塞(传统阻塞型), 服务器实现模式为一个连接一个线程, 即客户端有连接请求时服务器
端就需要启动一个线程进行处理, 如果这个连接不做任何事情会造成不必要的线程开销。
3.Java NIO : 同步非阻塞, 服务器实现模式为一个线程处理多个请求(连接), 即客户端发送的连接请求都会注
册到多路复用器上, 多路复用器轮询到连接有 I/O 请求就进行处理 【简单示意图】
4.Java AIO(NIO.2) : 异步非阻塞, AIO 引入异步通道的概念, 采用了 Proactor 模式, 简化了程序编写, 有效
的请求才启动线程, 它的特点是先由操作系统完成后才通知服务端程序启动线程去处理, 一般适用于连接数较
多且连接时间较长的应用
对 BIO 编程流程的梳理
1) 服务器端启动一个 ServerSocket。
2) 客户端启动 Socket 对服务器进行通信, 默认情况下服务器端需要对每个客户 建立一个线程与之通讯。
3) 客户端发出请求后, 先咨询服务器是否有线程响应, 如果没有则会等待, 或者被拒绝。
4) 如果有响应, 客户端线程会等待请求结束后, 在继续执行 。
代码:
package com.atguigu.bio; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class BIOServer { public static void main(String[] args) throws Exception { //线程池机制 //思路 //1. 创建一个线程池 //2. 如果有客户端连接,就创建一个线程,与之通讯(单独写一个方法) ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); //创建ServerSocket ServerSocket serverSocket = new ServerSocket(6666); System.out.println("服务器启动了"); while (true) { System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName()); //监听,等待客户端连接 System.out.println("等待连接...."); final Socket socket = serverSocket.accept(); System.out.println("连接到一个客户端"); //就创建一个线程,与之通讯(单独写一个方法) newCachedThreadPool.execute(new Runnable() { public void run() { //我们重写 //可以和客户端通讯 handler(socket); } }); } } //编写一个handler方法,和客户端通讯 public static void handler(Socket socket) { try { System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName()); byte[] bytes = new byte[1024]; //通过socket 获取输入流 InputStream inputStream = socket.getInputStream(); //循环的读取客户端发送的数据 while (true) { System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName()); System.out.println("read...."); int read = inputStream.read(bytes); if(read != -1) { System.out.println(new String(bytes, 0, read )); //输出客户端发送的数据 } else { break; } } }catch (Exception e) { e.printStackTrace(); }finally { System.out.println("关闭和client的连接"); try { socket.close(); }catch (Exception e) { e.printStackTrace(); } } } }
java BIO 问题分析
1) 每个请求都需要创建独立的线程, 与对应的客户端进行数据 Read, 业务处理, 数据 Write 。
2) 当并发数较大时, 需要创建大量线程来处理连接, 系统资源占用较大。
3) 连接建立后, 如果当前线程暂时没有数据可读, 则线程就阻塞在 Read 操作上, 造成线程资源浪费