Java 标准IO框架与NIO框架详解

    在看这篇文章之前,可以先去看看我博客中另一篇关于同步与异步、阻塞与非阻塞的理解

Java标准IO(BIO)

    BIO全称Blocking IO又叫做同步阻塞IO,它存在如下特点:

  • 面向流
  • 同步
  • 阻塞
package com.xdong.bio.client;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class BioClient {
	
	public static void main(String[] args) {
		Socket client = null;
		InputStream inputStream = null;
		OutputStream outputStream = null;
		try {
			client = new Socket("127.0.0.1", 8888);
			inputStream = client.getInputStream();
			outputStream = client.getOutputStream();
			outputStream.write("Hello Server I am conneting you!".getBytes());
			outputStream.flush();
			byte[] bytes = new byte[1024];
			while (inputStream.read(bytes) > 0) {//这里必须一次读完才能进行下次读操作,否则下一次会将上一次的
				String msg = new String(bytes).trim();
				System.out.println("client receive the message of server:" + msg);
				outputStream.write("client get the message !".getBytes());
				outputStream.flush();
				Thread.sleep(1*1000);
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
}
package com.xdong.bio.server;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BioServer {
	private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(5);
	
	public static void main(String[] args) {
		ServerSocket server = null;
		Socket socket = null;
		try {
			server = new ServerSocket(8888);
			while (!Thread.interrupted()) {
				socket = server.accept();
				THREAD_POOL.submit(new SocketHandler(socket));
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
	
	static class SocketHandler implements Runnable{
		private Socket socket;
		public SocketHandler(Socket socket) {
			super();
			this.socket = socket;
		}
		@Override
		public void run() {
			InputStream inputStream = null;
			OutputStream outputStream = null;
			try {
				inputStream = socket.getInputStream();
				outputStream = socket.getOutputStream();
				byte[] bs = new byte[1024];
				while (true) {
					int available = inputStream.available();// 输入流中有多少可用字节
					inputStream.read(bs, 0, available);//  从输出流中将数据写入字节数组(注:如果字节数组原本有100个字节,本次读取50个字节,那么它只会覆盖前面50个字节数据,字节数组中还是有100个字节数据)
					String receive = new String(bs, 0,available).trim();
					System.out.println("server receive the message of client:" + receive);
					outputStream.write("server get the message!".getBytes());//write函数-阻塞
					outputStream.flush();
					Thread.sleep(1*1000);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

     简单分析一下上面代码:

  1. 绑定一个端口,产生一个SocketServer对象,并调用accept()方法,监听这个端口,接收客户端连接。
  2. 当有客户端接入后,服务端单独起一个线程,利用socket与客户端交互。
  3. 利用socket.getInputStream流接收客户端发送的消息,利用socket.getOutputStream流发送消息给客户端。
  4. 注意:server.accept、inpustream.read、outputstream.write都会阻塞线程。
  5. 同步体现在服务端与客户端的交互只能一步步顺序进行。阻塞主要体现在第4点中的几个方法。即使采用了多线程技术,实现了主线程非阻塞,但是子线程中与客户端交互依旧是阻塞的。

Java NIO框架

    NIO,全称是Non-Blocking IO或New IO,解释就是非阻塞IO和新版IO。下面将给出三份不同的Server端代码,分别表示为:单Selector-单线程模式、单Selector-多线程模式、多Selector-多线程模式。

    NIO的特性如下:

  • 面向缓冲区
  • 同步
  • 非阻塞
package com.xdong.nio.v1.server;

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;

/**
 * v1=单线程、单reactor
 * @author xiongchaoqun
 * @date 2018年7月2日
 */
public class NioServer {

	public static void main(String[] args) {
		ServerSocketChannel serverSocketChannel = null;// 声明服务端Channel
		try {
			serverSocketChannel = ServerSocketChannel.open();// 实例化
			serverSocketChannel.bind(new InetSocketAddress(1234));// 绑定端口
			Selector selector = Selector.open();// 实例化一个Selector
			serverSocketChannel.configureBlocking(false);//设置非阻塞,才能进行下面的register
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 注册可接受连接监听
			while (selector.select() > 0) {
				Iterator<SelectionKey> it = selector.selectedKeys().iterator();
				while (it.hasNext()) {
					SelectionKey key = it.next();
					it.remove();// 如果不移除,那么下次遍历还会处理这个selectionKey
					if (key.isAcceptable()) {
						handleAcceptEvent(selector,key);
					} else if (key.isReadable()) {
						handleReadEvent(selector,key);
					}
					Thread.sleep(1000);
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 处理客户端接入事件
	 * 
	 * @param key
	 * @throws IOException
	 */
	private static void handleAcceptEvent(Selector selector,SelectionKey key) throws IOException {
		SocketChannel client = null;// 客户端
		ServerSocketChannel server = (ServerSocketChannel) key.channel();// 服务端
		try {
			client = server.accept();// 客户端接入产生一个SocketChannel
			System.out.println("server receive a connet request of client!");
			if (client == null) {
				return;
			}
			client.configureBlocking(false);// 设置客户端通道为非阻塞
			client.register(selector, SelectionKey.OP_READ);//一般不会主动设置SelectionKey.OP_WRITE,因为缓冲区会一直处于可写状态,无限触发select()
		} catch (Exception e) {
			server.close();
		}
	}

	/**
	 * 处理读监听事件
	 * @param key
	 * @throws IOException
	 */
	private static void handleReadEvent(Selector selector,SelectionKey key) throws IOException {
		SocketChannel client = (SocketChannel) key.channel();
		ByteBuffer readBuffer = ByteBuffer.allocate(1024);
		client.read(readBuffer);
		byte[] data = readBuffer.array();
		String msg = new String(data).trim();//消除空格
		System.out.println("server receive the client msg:" + msg);
		String outMsg = "I am the server!";
		ByteBuffer writeBuffer = ByteBuffer.wrap(outMsg.getBytes());
		client.write(writeBuffer);
	}

}
package com.xdong.nio.v2.server;

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;

/**
 * v2=线程池、单reactor
 * @author xiongchaoqun
 * @date 2018年7月2日
 */
public class NioServer {

	public static void main(String[] args) {
		ServerSocketChannel serverSocketChannel = null;// 声明服务端Channel
		try {
			serverSocketChannel = ServerSocketChannel.open();// 实例化
			serverSocketChannel.bind(new InetSocketAddress(1234));// 绑定端口
			Selector selector = Selector.open();// 实例化一个Selector
			serverSocketChannel.configureBlocking(false);//设置非阻塞,才能进行下面的register
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 注册可接受连接监听
			while (selector.select() > 0) {
				Iterator<SelectionKey> it = selector.selectedKeys().iterator();
				while (it.hasNext()) {
					SelectionKey key = it.next();
					it.remove();// 如果不移除,那么下次遍历还会处理这个selectionKey
					if (key.isAcceptable()) {
						handleAcceptEvent(selector,key);
					} else if (key.isReadable()) {
						new Processor().process(key);//使用线程池处理
					}
					Thread.sleep(1000);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 处理客户端接入事件
	 * 
	 * @param key
	 * @throws IOException
	 */
	private static void handleAcceptEvent(Selector selector,SelectionKey key) throws IOException {
		SocketChannel client = null;// 客户端
		ServerSocketChannel server = (ServerSocketChannel) key.channel();// 服务端
		try {
			client = server.accept();// 客户端接入产生一个SocketChannel
			System.out.println("server receive a connet request of client!");
			if (client == null) {
				return;
			}
			client.configureBlocking(false);// 设置客户端通道为非阻塞
			client.register(selector, SelectionKey.OP_READ);//一般不会主动设置SelectionKey.OP_WRITE,因为缓冲区会一直处于可写状态,无限触发select()
		} catch (Exception e) {
			server.close();
		}
	}

	/**
	 * 处理读监听事件
	 * @param key
	 * @throws IOException
	 */
	private static void handleReadEvent(Selector selector,SelectionKey key) throws IOException {
		SocketChannel client = (SocketChannel) key.channel();
		ByteBuffer readBuffer = ByteBuffer.allocate(1024);
		client.read(readBuffer);
		byte[] data = readBuffer.array();
		String msg = new String(data).trim();//消除空格
		System.out.println("server receive the client msg:" + msg);
		String outMsg = "I am the server!";
		ByteBuffer writeBuffer = ByteBuffer.wrap(outMsg.getBytes());
		client.write(writeBuffer);
	}

}

package com.xdong.nio.v3.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * v3=线程池+双reactor(双Selector)
 * @author xiongchaoqun
 * @date 2018年7月2日
 */
public class NioServer {

	public static void main(String[] args) {
		ServerSocketChannel serverSocketChannel = null;// 声明服务端Channel
		try {
			serverSocketChannel = ServerSocketChannel.open();// 实例化
			serverSocketChannel.bind(new InetSocketAddress(1234));// 绑定端口
			Selector selector = Selector.open();// 实例化一个Selector
			serverSocketChannel.configureBlocking(false);//设置非阻塞,才能进行下面的register
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 注册可接受连接监听
			
		    int coreNum = 2;
		    Processor[] processors = new Processor[coreNum];
		    for (int i = 0; i < processors.length; i++) {
		    	processors[i] = new Processor();
		    }
		    int acceptNum = 0;
			while (selector.select() > 0) {
				Iterator<SelectionKey> it = selector.selectedKeys().iterator();
				while (it.hasNext()) {
					SelectionKey key = it.next();
					it.remove();// 如果不移除,那么下次遍历还会处理这个selectionKey
					if (key.isAcceptable()) {
						acceptNum++;
						SocketChannel channel = null;// 客户端
						ServerSocketChannel server = (ServerSocketChannel) key.channel();// 服务端
						channel = server.accept();// 客户端接入产生一个SocketChannel
						System.out.println("server receive a connet request of client!");
						Processor processor = processors[acceptNum%coreNum];
						System.out.println(1);
						processor.addChannel(channel);//将SocketChannel交给另一个Selector去处理
						System.out.println(142);
						processor.wakeup();
					} 
					Thread.sleep(1000);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

package com.xdong.nio.v2.client;

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.SocketChannel;
import java.util.Iterator;

/**
 * v2=线程池、单reactor
 * @author xiongchaoqun
 * @date 2018年7月2日
 */
public class NioClient {
	
	public static void main(String[] args) throws Exception{
		SocketChannel socketChannel = null;
		socketChannel = SocketChannel.open();
		Selector selector = Selector.open();
		socketChannel.configureBlocking(false);//设置非阻塞,这样才能使用Selector
		socketChannel.connect(new InetSocketAddress(1234));//客户端连接--这里其实客户端并没有完成连接,是在channel.finishConnect();才能完成连接
		socketChannel.register(selector, SelectionKey.OP_CONNECT);//注册可连接事件
		while (selector.select() > 0) {
			Iterator<SelectionKey>it = selector.selectedKeys().iterator();
			while (it.hasNext()) {
				SelectionKey key = (SelectionKey) it.next();
				it.remove();
				if (key.isConnectable()) {
					handleConnectEvent(selector, key);
				}else if(key.isReadable()) {
					handleReadEvent(selector, key);
				}
				Thread.sleep(1000);
			}
		}
	}
	
	
	public static void handleConnectEvent(Selector selector,SelectionKey key) throws IOException {
		SocketChannel client = (SocketChannel) key.channel();
		if (client.isConnectionPending()) {
			client.finishConnect();
		}
		client.configureBlocking(false);//设置通道为非阻塞
		client.write(ByteBuffer.wrap(new String("I want to connet to the server!").getBytes()));//服务端  读操作会读到
		client.register(selector, SelectionKey.OP_READ);//注册可读时间
	}
	
	public static void handleReadEvent(Selector selector,SelectionKey key) throws IOException {
		SocketChannel client = (SocketChannel) key.channel();
		ByteBuffer buffer = ByteBuffer.allocate(1024);//声明一个bytebuffer
		client.read(buffer);
		byte[] bytes = buffer.array();
		String getMsg = new String(bytes).trim();
		System.out.println("client receive server msg is "+getMsg);
		client.write(ByteBuffer.wrap(new String("I am a client!").getBytes()));
	}
}
    Demo项目代码已上传至我的Git上,有兴趣的可以去fork一下。


猜你喜欢

转载自blog.csdn.net/qq824570123/article/details/80444342