1.介绍
- 是JBOSS通过的java开源框架
- 是异步的,基于事件驱动(点击一个按钮调用某个函数)的网络应用框架,高性能高可靠的网络IO程序
- 基于TCP,面向客户端高并发应用/点对点大量数据持续传输的应用
- 是NIO框架 (IO的一层层封装) TCP/IP->javaIO和网络编程–>NIO—>Netty
2.应用场景
- 互联网 RPC框架比如阿里的Dubbo
- 网络游戏 可以定制TCP/UDP 和http协议栈
- 大数据 hadoop序列化组件和实时数据文件共享 AVRO
还有Flink Spark Akka…其他开源项目
3.IO模型
- BIO(blocking原生javaIO,阻塞性,一个连接需要一个线程处理,连接不使用阻塞也占用线程)
//面试: 适用连接数目少架构稳定的,对服务器资源要求高,但程序简单容易理解 jdk1.4之前唯一的选择- NIO(No blocking/new 同步非阻塞)(一个线程使用selector维护多个客户端,轮询检测活动 多路复用,可以开多个线程) 图1nettyNIO原理、
//适用 连接数目多且连接比较短的(轻操作)的结果 比如聊天,弹幕,服务器之间通信,编程复杂 jdk1.4开始支持
3. AIO(等待时间长才需要异步,异步非阻塞) 是有效请求才启动线程
//面试: 适合;连接数量多且连接长的, 比如相册服务器.编程复杂,jdk7开始支持
4.BIO实例(可以使用线程池改善)
win的 telnet 127.0.0.1 6666端口的ctrl+]的send xxx可以发送数据到服务器
5.NIO
- 结构 如果通道没有事件,选择器不处理,线程也不阻塞 selector(选择器)<–>channel(通道)<—> buffer(缓冲区,数据先在这里实现了非阻塞)<—>socket //IntBuffer的使用
java除了boolean类型,是类型统一管理,所以比BIO更快 //!!!一定要flip()读写切换 才能读取值- 关系
1.buffer是内存块(面向缓冲/块的编程),是一个数组,达到一定量才给channel
2.selector由哪个channel有事件才切换过去的,不是自动切换的 ?
3.buffer channel是双向的,区别BIO 有in output流
4.一个thread对应一个selector,对应多个channel
6.Buffer的属性
position
filp()会使他变为0,读写的时候也会改变,一直往后移
limit(块的最大容量) 和capacity(自己设置的块容量)一样用来判断是否超出范围 capacity 自己设置的数组容量 mark 标记
//常用的api
IntBuffer in=IntBuffer.allocate(5);//5个容量
.flip //读写切换,如果是读的可以切换为写的,所以说NIO是双向的
.position(1);//设置读写的位置
.limit(3);//设置读写限制个数<3
.get(); //得到元素通过iterator
.put(1);
.put(1,1);//在指定位置放数据
.clear();//清除缓冲器
.hasRemain();//相当于hasNext()
.isReadOnly();//是否为只读缓冲区
//抽象方法jdk1.6引入 hasArray()缓冲区是否可访问底层的buffer数组 array()返回buffer数组
//常用ByteBuffer
7.Channel 可以向buffer读写和拷贝
1.ServerSockerChannel 类似ServerSocker,SockerChannel就是Socket,还有一模一样的UDP的类
2.FileChannel类
read() 通道读数据到buffer write() buffer写入通道 transferFrom()目标通道复制到本通道 transferTo() channel复制到目标通道
8.使用channel生成文件,与BIO的关系,底层就是BIO
String str="aaa";
out=new FileOutputStream("d;\\aaa.txt"); //输出流包括channel
FileChannel channel=out.getChannel();
byteBuffer=ByteBuffer.allocate(1024);
byteBuffer.put(str.getBytes);
byteBuffer.flip(); //初始状态是读,切换成写
channel.write(byteBuffer);//向其他通道写
out.close();//关闭流
public class bufferDemo {
public static void main(String[] args) throws IOException {
String str="helloworld";
FileOutputStream out = new FileOutputStream("D:\\abs.txt");
FileChannel channel = out.getChannel();
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
byteBuffer.position(2);
byteBuffer.put(str.getBytes()); //放在limit后面会报错,这个是限制容量
//相当于截断写入的数据
byteBuffer.limit(7);
//必须要写
byteBuffer.flip();
channel.write(byteBuffer);
channel.close();
out.close();
}
}
9.读数据
File file=new File("d:\\aa.txt");
in=new FileInputStream(file);
fileChannel=in.getChannel();
byteBuffer= ByteBuffer.allocate((int)file.length());
fileChannel.read(byteBuffer);
sout(new String( byteBuffer.array()));
in.close();
public class bfDemo {
public static void main(String[] args) throws IOException {
File file = new File("D:\\abs.txt");
FileInputStream inputStream = new FileInputStream(file);
FileChannel channel = inputStream.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
channel.read(byteBuffer);
System.out.println(new java.lang.String(byteBuffer.array()));
}
}
10.一个buffer 多个channel(创建两个流)拷贝文件 边读边写
//在java写路径相对位置是 本项目最父类的项目路径!!!(以前写相对路径总是出错)
//必须需要不然position=limit
byteBytebuffer.clear();
.flip();
public class bfDemo3Copy {
public static void main(String[] args) throws IOException {
FileInputStream inputStream = new FileInputStream("D:\\abs.txt");
FileChannel c1 = inputStream.getChannel();
FileOutputStream fileOutputStream = new FileOutputStream("D:\\abs1.txt");
FileChannel c2 = fileOutputStream.getChannel();
ByteBuffer allocate = ByteBuffer.allocate(inputStream.available());
c1.read(allocate);
allocate.flip();
c2.write(allocate);
}
}
//一块一块读,反转写入.steps are combinate bytes to buffer and send to another channel(outputStream)
public class NIOFileChannel03 {
public static void main(String[] args) throws Exception {
FileInputStream fileInputStream = new FileInputStream("1.txt");
FileChannel fileChannel01 = fileInputStream.getChannel();
FileOutputStream fileOutputStream = new FileOutputStream("2.txt");
FileChannel fileChannel02 = fileOutputStream.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
while (true) { //循环读取
byteBuffer.clear(); //清空buffer!!!
int read = fileChannel01.read(byteBuffer);
System.out.println("read =" + read);
if(read == -1) { //表示读完
break;
}
//将buffer 中的数据写入到 fileChannel02 -- 2.txt
byteBuffer.flip();
fileChannel02.write(byteBuffer);
}
//关闭相关的流
fileInputStream.close();
fileOutputStream.close();
}
}
11.拷贝图片
destch.transferFrom(sourceCh,0,sourceCh.size());
//关闭所有通道和流
public class bfDemo4transferCopy {
public static void main(String[] args) throws IOException {
FileInputStream inputStream = new FileInputStream("D:\\abs.txt");
FileChannel c1 = inputStream.getChannel();
FileOutputStream fileOutputStream = new FileOutputStream("D:\\abs3.txt");
FileChannel c2 = fileOutputStream.getChannel();
c2.transferFrom(c1,0,c1.size());
}
}
12.buffer放的顺序和取的顺序一样不然抛出异常BufferUnderflowException
ByteBuffer bf= ByteBuffer.allocate(64);
bf.putInt(100);
bf.putLong(100L);
bf.flip();
bf.getInt();
bf.getLong();
13.写完转为只读buffer
//12的代码
buffer.asReadOnlyBuffer();public class OnlyWrite {
public static void main(String[] args) {
ByteBuffer bf= ByteBuffer.allocate(64);
bf.putInt(100);
bf.putLong(100L);
bf.flip();
ByteBuffer byteBuffer = bf.asReadOnlyBuffer();
byteBuffer.getInt();
byteBuffer.getLong();
byteBuffer.putLong(10);//错误不能写,只能读
}
}
14.MappedByteBuffer可以在堆外内存修改文件(效率高)操作系统不用拷贝一次到内存
access=new RandAccessFile("1.txt","rw");
channel=access.getChannel();
map=channel.map(FileChannel.MapMode.READ_WRITE,0,5);模式,开始位置,结束位置 <5byte
map.put(0,(byte)'H'); //在内存直接修改文件内容,然后放回磁盘
access.close();
public class MapBuffer {
public static void main(String[] args) throws IOException {
RandomAccessFile access = new RandomAccessFile("1.txt", "rw");
FileChannel channel = access.getChannel();
//到内存修改的大小范围是 0-5, if out of the bound will throw an expection
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
map.put(0,(byte)'A');
map.put(4,(byte)'H');
access.close();
System.out.println("修改成功");
}
}
//idea需要在文件夹外打开,不然看不到效果
15.分散和聚集 scatter(写的时候放到多个buffer[数组array])和gather(读的时候多个buffer) 加快读写效率(the key is using many buffer to read and read)(read and write only once that can get more buffer)
public class scatterAndGather {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocket = ServerSocketChannel.open().bind(new InetSocketAddress(6666));
ByteBuffer[] byteBuffers= new ByteBuffer[2];
byteBuffers[0]=ByteBuffer.allocate(3);
byteBuffers[1]=ByteBuffer.allocate(5);
SocketChannel accept = serverSocket.accept();
int msgLen=8;
while (true){
int byteRead=0;
while (byteRead<msgLen){
long read = accept.read(byteBuffers);
byteRead++;
System.out.println("bufferRead:"+byteRead);
Arrays.asList(byteBuffers).stream()
.map(
buffer->"position"+buffer.position()+
",limit="+buffer.limit()
)
.forEach(System.out::println);
}
Arrays.asList(byteBuffers).stream().forEach(buffer ->buffer.flip() );
long byteWrite=0;
while (byteWrite<msgLen){
//一次读出多少个字节
long l=accept.write(byteBuffers);
byteWrite+=l;
System.out.println(byteWrite);
}
Arrays.asList(byteBuffers).forEach(byteBuffer -> {
System.out.println(new String(byteBuffer.array() ));
byteBuffer.clear();});
System.out.println("byteRead:="+byteRead+"byteWrite:"+byteWrite+"msgLen:"+msgLen);
}
}
}
16.selector管理多个连接(channel) 不停如轮询检测事件,有事件处理,阻塞不等待,处理其他通道的请求,解决了多线程单通道的问题,提高了性能
selector类selects()//得到channel的连接的selectkey集合阻塞,
selector.select(1000)//阻塞1000毫秒,必须返回数据
selector.wakeup()//唤醒selector,阻塞中可以唤醒
selector.selectNow()//非阻塞,立即返回结果,核心方法
selector.selectedKeys() //select获取绑定channel产生事件的selectkey集合
//可以注册channel到selector上返回selectionKey (集合)