Netty的介绍
Netty是异步的(指定回调处理)、基于事件驱动的网络应用框架,用于快速开发高性能、高可靠性的网络IO程序。
Netty本质是一个NIO框架,适用于服务器通讯相关的多种应用场景,分布式节点远程调用中Netty往往作为RPC框架使用,如Dubbo
层级结构
Netty
NIO(io、网络)
jdk原生io编程和网络编程
最底层是tcp+ip
IO模型:
BIO(原生的javaIO,同步并阻塞)
同步并阻塞:客户端一个连接请求对应一个线程。如果连接非常多,那么线程会非常多,对服务器压力增大
BIO使用场景:适合用连接数小且固定的架构,对服务器资源要求高

BIO编程
1.服务端启动一个ServerScoket
2.客户端启动Socket对服务器进行通信,默认情况下服务器端需要对每个客户端建立一个线程与之通信
3.客户端发出请求后,先咨询服务器是否有线程响应
如果没有则会等待或者被拒绝;
如果有响应,则客户端线程会等待请求结束后,再继续执行。(有阻塞)
public class BIOServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服务器启动了~~~");
//1.创建线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
//2.有客户端连接,就创建一个线程,与之通信
while (true){
//监听,等待客户端连接
final Socket socket = serverSocket.accept();
System.out.println("连接到一个客户端");
threadPool.execute(new Runnable() {
@Override
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];
InputStream inputStream = socket.getInputStream();
//循环读取客户端数据到bytes数组
while(true){
//len是读入缓存区的字节总数
int len = inputStream.read(bytes);
if(len !=-1){
//!=-1 表示可以继续读
// 输出客户端发送的数据
System.out.println(new String(bytes,0,len));
}else {
//读取完毕跳出循环
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关闭socket连接
System.out.println("关闭和客户端的连接");
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
win+r打开小黑框
telnet 127.0.0.1 6666
如果cmd提示:telnet不是内部或外部命令,也不是可运行的程序怎么办
控制面板-程序和功能-启用或关闭Windows功能-从列表中选中“Telnet客户端”项,点击“确定”按钮

键入:ctrl+]
send命令发送数据:send xiaoyumao

在idea的控制台打印如下

BIO存在的问题:
当并发数较大时。需要创建大量线程来处理连接,系统资源占用较大
连接建立后,如果当前线程暂时没有数据可读,则线程会阻塞在read操作,造成线程资源浪费
NIO(同步非阻塞)
同步非阻塞:一个线程处理多个连接请求,所有连接请求都会注册到多路复用器Selector上,多路复用器轮询到连接有IO请求就进行处理
NIO使用场景:适用于连接数目多且连接比较短的架构

NIO有3大核心组件,Selector、Channel、Buffer
Buffer和通道可以相互读写,程序和Buffer交互,所以NIO是面向缓冲区的编程

每个Channel对应一个Buffer
每个Selector对应一个线程,一个线程对应多个channel(连接)
程序切换到哪个Channel是由事件Event决定的,Selector会根据不同的事件,在各个通道上切换
Buffer是一个内存块,底层是一个数组
NIO数据的读取和写入都是通过Buffer,这是和BIO的本质不同。
BIO中要么是输入流要么是输出流,不是双向的。NIO中Buffer可以读可以写,需要flip切换
AIO(异步非阻塞)
异步非阻塞:引入异步通道,采用Proacor模式。
AIO使用场景:适用于连接数目多且连接比较长的架构
NIO-Buffer
BIO以流的方式处理数据,NIO以块(面向Buffer)处理数据,NIO效率比BIO高很多,且NIO是非阻塞的,而BIO是阻塞的
Buffer类和常用子类
Buffer是一个抽象类,常用子类如下

网络传输都是用字节传输的,故用的最多的ByteBuffer:存储字节数据到缓冲区

ByteBuffer的常用方法

Buffer的4个标志位
public abstract class Buffer {
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
}
capacity:容量,在缓冲区创建时被设定且不能改变
limit:缓冲区的当前终点,不能对>=limit的位置读写操作
position:下一个要被读或写的元素索引
mark:标记(很少修改)
flip方法的作用
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
IntBuffer的基本使用
public class BasicBuffer {
public static void main(String[] args) {
//创建一个大小为5的buffer,可以存放5个int
IntBuffer intBuffer = IntBuffer.allocate(5);
//向buffer存放数据
intBuffer.put(1);
intBuffer.put(2);
intBuffer.put(3);
intBuffer.put(4);
intBuffer.put(5);
//将intBuffer读写切换
intBuffer.flip();
//从buffer读取数据
while (intBuffer.hasRemaining()){
//每取一次,则指针后移一位
System.out.println(intBuffer.get());
}
}
}
idea运行效果:
