基础
I/O输入/输出(Input/Output),提供对文件读写操作
按流向分:
输入流:程序可以从中读取数据的流。
输出流:程序能向其中写入数据的流。
按数据传输单位分:
字节流:以字节为单位传输数据的流
字符流:以字符为单位传输数据的流
InputStream 字节输入流超类
OutputStream 字节输出流超类
Read 字符输入流超类
Writer 字符输出流超类
BIO(同步阻塞)
通过下面的测试很容易就能看出来在serverSocket.accept()和in.read(by)时发生线程阻塞情况,这段时间线程占用系统资源却没有工作,给系统造成不必要的资源开销。即使采用多线程in.read(by)也会在子线程中发生阻塞,线程数量过多时系统负担太大,并且数据读取缓慢
public class Server {
public static void main(String[] args) throws Exception{
ServerSocket serverSocket = new ServerSocket(8181);
System.out.println("等待客户端连接");
Socket socket = serverSocket.accept();
InputStream in = socket.getInputStream();
byte[] by = new byte[1024];
System.out.println("等待客户端传输数据");
in.read(by);
System.out.println(new String(by));
OutputStream out = socket.getOutputStream();
Thread.sleep(5000);
out.write("测试反馈".getBytes());
}
}
public class Client {
public static void main(String[] args) throws Exception{
Socket socket = new Socket("127.0.0.1", 8181);
OutputStream out = socket.getOutputStream();
Thread.sleep(5000);
out.write("这是一个测试".getBytes());
InputStream in = socket.getInputStream();
byte[] by = new byte[1024];
System.out.println("等待服务端传输数据");
in.read(by);
System.out.println("客户端接收数据:" + new String(by));
}
}
NIO(同步非阻塞)
NIO中要提供了Selector、Channel和Buffer三个核心组件
Selector:可以绑定多个Channel通道并监听通道事件,Channel通道事件有连接、接收、读和写事件
Channel:是NIO传输数据的通道,可以向通道读数据和写数据
Buffer:NIO基于Channel通道和Buffer缓存区进行数据传输,在通道中读写数据使用的就是Buffer缓存区
NIO主要是通过遍历所有注册在Selector选择器上并发生事件的Channel通道,根据判断发生事件类型进行不同操作
public class Server {
public static void main(String[] args) throws Exception{
/* 配置服务器Socket通道 */
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
/* 设置非阻塞模式 */
serverSocketChannel.configureBlocking(false);
/* 绑定端口 */
serverSocketChannel.socket().bind(new InetSocketAddress(1212));
/* 配置选择器 */
Selector selector = Selector.open();
/* 设置指定通道的监听模式 */
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true){
/* 判断是否存在准备好IO操作的通道 */
if(selector.select() == 0){
continue;
}
System.out.println("触发事件");
/* 遍历所有准备好的通道 */
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
/* 获取后的通道要清除,否则会重复遍历 */
iterator.remove();
/* 客户端准备连接,开始配置 */
if(selectionKey.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
/* 获取客户端通道 */
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
/* 设置客户端通道监听模式 */
socketChannel.register(selector, SelectionKey.OP_READ);
}
/* 准备读取客户端发送过来的数据 */
else if(selectionKey.isReadable()){
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
try {
/* 输出数据发送给客户端 */
socketChannel.write(ByteBuffer.wrap("服务器返回数据".getBytes()));
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int x = socketChannel.read(byteBuffer);
if(x > 0){
System.out.println(new String(byteBuffer.array()));
}
else {
socketChannel.close();
}
}
catch (Exception e){
try {
socketChannel.close();
}
catch (Exception e1){
}
}
}
}
}
}
}
public class Client {
public static void main(String[] args) throws Exception{
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 1212));
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
while (true){
if(selector.select() == 0){
continue;
}
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
iterator.remove();
/* 判断与服务器连接是否完成 */
if(selectionKey.isConnectable()){
SocketChannel server = (SocketChannel) selectionKey.channel();
/* 判断Socket通道连接是否完成,配置监听模式 */
if(server.finishConnect()){
socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);
socketChannel.write(ByteBuffer.wrap("客户端返回数据".getBytes()));
}
}
else if(selectionKey.isReadable()){
SocketChannel socket = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
socket.read(byteBuffer);
System.out.println(new String(byteBuffer.array()));
}
}
}
}
}
AIO(异步非阻塞)
等IO操作完成之后才会通知线程,由系统自动触发,AIO操作基于事件和回调机制