BIO
同步阻塞式IO,在while循环中服务端会调用accept方法等待接收客户端的连接请求,一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成。
如果BIO要能够同时处理多个客户端请求,就必须使用多线程,即每次accept阻塞等待来自客户端请求,一旦受到连接请求就建立通信套接字同时开启一个新的线程来处理这个套接字的数据读写请求,然后立刻又继续accept等待其他客户端连接请求,即为每一个客户端连接请求都创建一个线程来单独处理.虽然此时服务器具备了高并发能力,即能够同时处理多个客户端请求了,但是却带来了一个问题,随着开启的线程数目增多,将会消耗过多的内存资源,导致服务器变慢甚至崩溃,NIO可以一定程度解决这个问题。
NIO
同步非阻塞式IO,关键是采用了事件驱动的思想来实现了一个多路转换器。
NIO与BIO最大的区别就是只需要开启一个线程就可以处理来自多个客户端的IO事件,这是怎么做到的呢?
就是多路复用器,可以监听来自多个客户端的IO事件:
A. 若服务端监听到客户端连接请求,便为其建立通信套接字(java中就是通道),然后返回继续监听,若同时有多个客户端连接请求到来也可以全部收到,依次为它们都建立通信套接字。
B. 若服务端监听到来自已经创建了通信套接字的客户端发送来的数据,就会调用对应接口处理接收到的数据,若同时有多个客户端发来数据也可以依次进行处理。
C. 监听多个客户端的连接请求和接收数据请求同时还能监听自己时候有数据要发送。
Buffer的常用子类(它们之间最大区别在于底层实现数组的数据类型):
ByteBuffer:存储字节数据到缓冲区
CharBuffer:存储字符数据到缓冲区
IntBuffer:存储整型数据到缓冲区
ShortBuffer:存储短整型数据到缓冲区
LongBuffer:存储长整型数据到缓冲区
FloatBuffer:存储浮点型数据到缓冲区
DoubleBuffer:存储双精度浮点型数据到缓冲区
NIO文件操作:
import com.sun.org.apache.xpath.internal.operations.String;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NioChannel {
public static void main(String[] args) throws IOException {
样例1 FileOutputStream fos=new FileOutputStream("data01.txt");
FileChannel channel=fos.getChannel();
ByteBuffer buffer= ByteBuffer.allocate(1024);
buffer.put("这是NIO测试锕".getBytes());
buffer.flip(); //这里是因为写入到缓冲区指针也后移,如果想写出来指针就必须复位到0
channel.write(buffer);
System.out.println("数据写入完成");
样例2 FileInputStream fin=new FileInputStream("data01.txt");
FileChannel channel =fin.getChannel(); //获取通道
ByteBuffer buffer=ByteBuffer.allocate(1024); //定义缓存区
channel.read(buffer); //通道读取数据到缓存区
String str=new String(buffer.array()); //输出缓存区数据
System.out.println(str);
}
}
聚集与分散:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class Test {
public static void main(String[] args) throws IOException {
FileInputStream is = new FileInputStream("data01.txt");
FileOutputStream out = new FileOutputStream("data02.txt");
//1.获取管道
FileChannel ischannel = is.getChannel();
FileChannel outchannel = out.getChannel();
//2.定义多个缓冲区,做数据分散
ByteBuffer buf1 = ByteBuffer.allocate(10);
ByteBuffer buffer = ByteBuffer.allocate(1024);
ByteBuffer[] byteBuffers = {buf1, buffer};
//3.从通道中读取数据分散到各个缓冲区
ischannel.read(byteBuffers);
//4.从每个缓冲区中查询是否读取到了
for (ByteBuffer buf : byteBuffers) {
buf.flip();
System.out.println(new String(buf.array(),0,buf.remaining()));
// outchannel.write(buf);
//buf.clear();
}
//聚集写入到通道
outchannel.write(byteBuffers);
ischannel.close();
outchannel.close();
}
}
Server:
package Nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
* 非阻塞服务端开发
*/
public class Server {
public static void main(String[] args) throws IOException {
System.out.println("服务端启动成功");
//1.获取服务端通道,这个服务端通道获取与客户端的通信,接收连接请求
ServerSocketChannel ssChannel=ServerSocketChannel.open();
//2.切换为非阻塞式模式
ssChannel.configureBlocking(false);
//3.绑定连接的端口
ssChannel.bind(new InetSocketAddress(9999));
//4.获取一个选择器来轮循通道
Selector selector=Selector.open();
//5.将通道注册到选择器上,并且开始指定监听事件
//注册当前服务端的通道,来监听有没有客户端的新连接
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//6.使用选择器来查看有没有就绪的事件
while(selector.select()>0){ //这是阻塞式的事件大于0
// 7.用迭代器来遍历选择器就绪的事件(连接或者读取)
System.out.println("开始一轮事件处理");
Iterator <SelectionKey> it=selector.selectedKeys().iterator();
while(it.hasNext()){
System.out.println("这是服务端你的读事件");
SelectionKey key=it.next(); //8.拿到当前的事件
if(key.isAcceptable()){ //9.如果当前事件是接入事件
SocketChannel sChannel=ssChannel.accept(); //10.那就接入一个客户端通道
//11.将客户端连接进来的通道也配置为非阻塞式通道
sChannel.configureBlocking(false);
//12.将客户端连接进来的通道设置为读监听,因为从客户端写过来,对于服务端就是读事件
sChannel.register(selector,SelectionKey.OP_READ);
} //如果监听的是读事件
else if(key.isReadable()){
//13.运用事件来反向获取客户端的这个通道
SocketChannel sChannel=(SocketChannel) key.channel();
//14.读取数据
System.out.println("这是服务端你的写事件");
ByteBuffer buf=ByteBuffer.allocate(1024);
int len=0;
//14.从客户端的通道读取数据到缓冲区
while((len=sChannel.read(buf))!=-1){
buf.flip(); //将缓冲区设置为读,位置清0归位,往外写
System.out.println(new String(buf.array(),0,len));
buf.clear();
}
}
//处理完毕这个事件移除当前事件
it.remove();
}
}
}
}
客户端:
package Nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
public class Client {
//1.获取通道
public static void main(String[] args) throws IOException {
SocketChannel sChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",9999));
//2.切换成阻塞模式
sChannel.configureBlocking(false);
//3.分配指定缓冲区大小
ByteBuffer buf=ByteBuffer.allocate(1024);
Scanner sc=new Scanner(System.in);
//4.发送数据给服务端
while(true){
System.out.println("请说");
String msg=sc.nextLine();
//这是写
buf.put(("波妞说:"+msg).getBytes());
//归为0并转换模式
buf.flip();
sChannel.write(buf);
buf.clear();
}
}
}