To grasp the difference between BIO and NIO

BIO and NIO compare

NIO (non blocking I / O) Non-blocking I / O, I jdk1.4 newly introduced / O, the usual contact file I / O operation is BIO, i.e. blocking I / O

BIO API Use

specific process:

A. Test blocking accept () method
public void testAccept() throws IOException{
	ServerSocket ss = new ServerSocket();
	ss.bind(new InetSocketAddress(9999));
	Socket sk = ss.accept();
	System.out.println("有连接连入");
}复制代码
JUnit test, "access connection" is not output, the accept () method to produce blocked.
B. then added connect () Code Test Method:
public void testContect() throws Exception{
	Socket sk = new Socket();
	sk.connect(new InetSocketAddress(
			"127.0.0.1", 9999));
	System.out.println("连接成功");
}复制代码
First run server-side method (testAccept ()), then run the client method, we discovered accept () method blocks released. In addition, "connection successful" correct output. If you do not start the server-side method first, and run the client directly method, first discovered blocking a moment, then JUnit test throws an exception.
Sum up: connect () method will produce obstruction, specify the connection is successful, the blocking was released.
Blocking accept () method produced, until the connection to the server is obtained, blocking is not released.
Obstructive C. test read () method
C1. Modified again testAccept () method
InputStream  in= sk.getInputStream();
byte bts[] = new byte[1024];
in.read(bts);
System.out.println("读取到了数据:"+new String(bts));复制代码
C2. In order not to interrupt the connection, you need to modify testConnect ()
whiletrue);复制代码

Summary: read () method will produce blocked until the read contents, blocking was released.
D. Obstructive test write () method
D1. Modify testAccept () method
for(int i =1;i<100000;i++){
	out.write("HelloWorld".getBytes());
	System.out.println(i);
}
System.out.println("数据写完了。。。");
}复制代码
Run the server-side method, then run the client method; i output value is found to 65,513, blocked.
        for(int i =1;i<200000;i++){
		out.write("Hello".getBytes());
		System.out.println(i);
	}复制代码
Trimming code output to 131,026 blocked.
Summary: When write () method will produce obstruction, write () Wang Chu has been written data, but none of the parties to read data, write up to a certain amount (I was 655130B, different computer may be different), resulting in blockage . Writing data to the card device buffer.

NIO-related API

Channel View API
ServerSocketChannel, SocketChannel NIO-based (based on tcp realized, based on security handshake)
DatagramChannel UDP-based protocol, unsafe

NIO-Channel API(上)

accept and connect using

/**ServerSocketChannel.open()创建服务器端对象
 * nio提供两种模式:阻塞模式和非阻塞模式
 * 默认情况下是阻塞模式。
 * 通过ssc.configureBlocking(false)设置为非阻塞模式
 * @throws Exception
 */
@Test
public void testAccept() throws Exception{
	//创建服务器端的服务通道
	ServerSocketChannel ssc = 
			ServerSocketChannel.open();
	//绑定端口号
	ssc.bind(new InetSocketAddress(8888));
	//设置非阻塞模式
	ssc.configureBlocking(false);
	//调用accpet方法获取用户请求的连接通到
	SocketChannel sc = ssc.accept();
	System.out.println("有连接连入");
}复制代码
Run found, and no output "connection access" channel provides non-blocking and blocking modes, the default mode is blocked. Ssc.configureBlocking may be added prior to bind port (false); non-blocking mode channel set. Run again "access connection" will be output.
public void testConnect() throws Exception{
	SocketChannel sc = SocketChannel.open();
	sc.configureBlocking(false);
	sc.connect(new InetSocketAddress("127.0.0.1", 8888));
	System.out.println("连接成功");
}复制代码
Before running the method throws an exception, and no output "connection successful", the channel Connect () method is also blocked;; was added sc.configureBlocking (false) using the method sc.configureBlocking (false); clients may be connected channel is set to a non-blocking mode.

read (), write () Test Method (excessive)

sc.read(ByteBuffer dst)
sc.write(ByteBuffer src)复制代码
Since both methods require ByteBuffer object as a parameter, so we need to talk about ByteBuffer buffer.

NIO-ByteBuffer buffer API

public class DemoByteBuffer {
	/**ByteBuffer缓冲区类,有三个重要的属性
	 * capacity	10:容量,该缓冲区可以最多保存10个字节
	 * position	0:表示位置
	 * limit 10:限制位(用在获取元素时限制获取的边界)	
	 */
	@Test
	public void testByteBuffer(){
		ByteBuffer buf = ByteBuffer.allocate(10);
		System.out.println();
	}
	/**put(byte bt)向缓存区中添加一个字节
	 *   每调用一次该方法position的值会加一。
	 */
	@Test
	public void testPut(){
		ByteBuffer buf = ByteBuffer.allocate(10);
		byte b1 = 1;
		byte b2 = 2;
		buf.put(b1);
		buf.put(b2);
		buf.putInt(3);
		System.out.println();
	}
	/**get()获取position指定位置的一个字节内容。
	 * 每调用一次该方法,position++;
	 * 如果在调用get()时,position>=limit,
	 * 则抛出异常BufferUnderflowException
	 * 
	 * position(int pt):设置position的值为pt
	 * position():获取当前缓冲区的position属性的值
	 * limit(int):设置限制为的值
	 * limit():获取当前缓冲区的limit属性的值。
	 */
	@Test
	public void testGet(){
		ByteBuffer buf = ByteBuffer.allocate(10);
		byte b1 = 1;
		byte b2 = 2;
		buf.put(b1);//1
		buf.put(b2);//2
		//设置position的值为0
		buf.position(0);
		//设置限制位(不想让用户获取无用的信息)
		buf.limit(2);
		System.out.println(buf.get());//
		System.out.println(buf.get());
		System.out.println(buf.get());
	}
	/**flip()方法:反转缓存区,一般用在添加完数据后。
	 * limit = position;将limit的值设置为当前position的值
       position = 0;再将position的值设置为0
	 */
	@Test
	public void testFlip(){
		ByteBuffer buf = ByteBuffer.allocate(10);
		byte b1 = 1;
		byte b2 = 2;
		buf.put(b1);//1
		buf.put(b2);//2
		/*buf.limit(buf.position());
		buf.position(0);*/
		buf.flip();
	}
	/**clear():"清除缓存区"
	 * 底层源代码:
	 *  position = 0;
        limit = capacity;
       	通过数据覆盖的方式达到清除的目的。
	 */
	@Test
	public void testClear(){
		ByteBuffer buf = ByteBuffer.allocate(10);
		byte b1 = 1;
		byte b2 = 2;
		buf.put(b1);//1
		buf.put(b2);//2
		buf.clear();
		byte b3=33;
		buf.put(b3);
		buf.flip();
		for(int i = 0;i<buf.limit();i++){
			System.out.println(buf.get());
		}
	}
	/**hasRemaining()判断缓冲区中是否还有有效的数据,有返回
	 * true,没有返回false
	 * public final boolean hasRemaining() {
	        return position < limit;
	   }
	 */
	@Test
	public void testClear12(){
		ByteBuffer buf = ByteBuffer.allocate(10);
		byte b1 = 1;
		byte b2 = 2;
		buf.put(b1);//1
		buf.put(b2);//2
		buf.clear();
		byte b3=33;
		buf.put(b3);
		buf.flip();
		/*for(int i = 0;i<buf.limit();i++){
			System.out.println(buf.get());
		}*/
		/*int i =0;
		while(i<buf.limit()){
			System.out.println(buf.get());
			i++;
		}*/
		while(buf.hasRemaining()){
			System.out.println(buf.get());
		}
	}
}复制代码

NIO-Channel API(下)

1, read () method
The method of modifying testAccept ChanelDemo class:
ByteBuffer buf = ByteBuffer.allocate(10);
sc.read(buf);
System.out.println("有数据读入:"+buf.toString());复制代码
testConnect () method without any modifications, run testAccept () method, found in sc.read (buf) line throws null pointer exception. buf target is unlikely to be null, so sc is null.
The biggest problem non-blocking programming: I do not know whether there are real client access, it is easy to generate a null pointer; it is necessary to set artificially blocked.
The SocketChannel sc = ssc.accept (); to:
while(sc==null){
	sc = ssc.accept();
}复制代码
Problems testAccept run again () method, a null pointer is resolved; then run testConnect () method, connection can be established normally found, but "the data is read out .." is not output, the service channel is provided even if non-ssc blocked, did not change obtained sc default channel blocking mode, so sc.read (buf) blocked. Or want to read () method blocks, you need to call read () before adding sc.configureBlocking (false); so even if the data is not read, "reads the data .." can also be printed.

2, write () method
Modified testContect () method, append the following code:
ByteBuffer buf = ByteBuffer.wrap("HelloWorld".getBytes());
sc.write(buf);复制代码
Test bug, first not to run server-side method, the direct method of running the client testConnect (), the output of "successful connection", but sc.write (buf) line throws NotYetConnectException exception. Why sc Thrown? Hang non-blocking mode very place that does not know the real connection is established. Modify testConnect ():
ByteBuffer buf = ByteBuffer.wrap("HelloWorld".getBytes());
while(!sc.isConnected()){
	sc.finishConnect();
}
sc.write(buf);复制代码
Run again testConnect (), before abnormal resolved, but there is a new exception:
java.net.ConnectException: Connection refused: no further information
	at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)复制代码
First start the server end (testAccept ()), after starting the client (testConnect ()) can be.
Handwriting difficult NIO non-blocking mode, the code is not the point, it is important that the leads design.

Selector design ideas

The introduction of the problem


BIO simulate what use to write code
(Write a server and client programs, run a server program that runs four times a client program to simulate four user thread)
public class BIOServer {
	public static void main(String[] args) throws Exception {
		ServerSocket ss = new ServerSocket();
		ss.bind(new InetSocketAddress(7777));
		while(true){
			Socket sk = ss.accept();
			new Thread(new ServiceRunner(sk)).start();
		}
	}
}
class ServiceRunner implements Runnable{
	private Socket sk;
	public ServiceRunner(Socket sk){
		this.sk = sk;
	}
	public void run(){
		System.out.println("提供服务的线程id:"+
				Thread.currentThread().getId());
		try {
			Thread.sleep(Integer.MAX_VALUE);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class BIOClient {
	public static void main(String[] args) throws Exception {
		Socket sk = new Socket();
		sk.connect(new InetSocketAddress("127.0.0.1", 7777));
		while(true);
	}
}复制代码

Server Startup
Responsible for client service, the current thread id: 9
Responsible for client service, the current thread id: 10
Responsible for client service, the current thread id: 11
Responsible for client service, the current thread id: 12



The disadvantage of this mode of analysis:
One disadvantage: each additional user requests, it will create a new thread to provide them with services. When a user requests a huge amount, the number of threads will be increases, in turn, increases memory usage, all the scenes not suitable for high concurrency, high access.
Shortcoming 2: Thread particularly large, not only takes up memory overhead, will take up a lot of cpu overhead, because the cpu do thread scheduling.
3 disadvantages: If a user is only connected to the operation, and for a long time do not do other operations, it will have a lot of idle threads. Cpu will do meaningless idling, reduce overall performance.
Shortcoming 4: This model causes the thread (user request) really need to be treated can not be processed in time.

Solution

3 for the shortcomings and weaknesses 4 can be idle threads to block state, cpu is not dispatched thread blocking state, avoiding the cpu idle. Therefore, the introduction of the event listener mechanism to achieve.
Selector multiplexer selector, play a role in the event listener.
Monitor which user performs an operation, it executes the corresponding thread wake. So what events have it?
Event: 1.accept event, 2.connect event, 3.read event, 4.write event


1 2 for the shortcomings and disadvantages, the model can be used to achieve non-blocking, even with a small number of threads a thread to handle multiple user requests. Note, however, this model is the use of scenarios for a large number of short scenes request. (Such as user access to electricity's website), the request is not suitable for long scenes (such as downloading large files, this scenario, NIO is not necessarily better than the BIO)


Extended knowledge
Thundering herd phenomenon, hidden: cpu load will gather rise in a short time, short Caton appear even crash when the most severe cases. The second problem is that performance is not high.

Selector API service channel

accept events

Write server-side program:
public class NIOServer {
	public static void main(String[] args) throws Exception {
		ServerSocketChannel ssc = ServerSocketChannel.open();
		ssc.bind(new InetSocketAddress(6666));
		//设置为非阻塞
		ssc.configureBlocking(false);
		//定义多路复用选择器
		Selector sel = Selector.open();
		//注册accept事件
		ssc.register(sel, SelectionKey.OP_ACCEPT);
		while(true){
			//select()在没有收到相关事件时产生阻塞,直到
			//有事件触发,阻塞才会得以释放
			sel.select();
			//获取所有的请求的事件
			Set<SelectionKey> sks = sel.selectedKeys();
			Iterator<SelectionKey> iter = sks.iterator();
			while(iter.hasNext()){
				SelectionKey sk = iter.next();
				if(sk.isAcceptable()){
					ServerSocketChannel ssc1= 
						(ServerSocketChannel)sk.channel();
					SocketChannel sc = ssc1.accept();
					while(sc==null){
						sc = ssc1.accept();
					}
					sc.configureBlocking(false);
					//为sc注册read和write事件
					//0000 0001  OP_READ
					//0000 0100  OP_WRITE
					//0000 0101  OP_READ和OP_WRITE
					sc.register(sel, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
					System.out.println("提供服务的线程id:"+
						Thread.currentThread().getId());
				}
				if(sk.isWritable()){
				}
				if(sk.isReadable()){
				}
                                iter.remove();
			}
		}
	}
}

编写客户端代码:
public static void main(String[] args) throws Exception {
		SocketChannel sc = SocketChannel.open();
		sc.connect(new InetSocketAddress("127.0.0.1", 6666));
		//sc.configureBlocking(false);
		System.out.println("客户端有连接连入");
while(true);
	}
}复制代码
The server initiates a client starts three times, the server console output:
The server initiates
There are connected to the client, responsible for handling the request thread id: 1
There are connected to the client, responsible for handling the request thread id: 1
There are connected to the client, responsible for handling the request thread id: 1
Processing a plurality of requests to use the same thread.
The design architecture applies only short of highly concurrent requests scene.

read event

Modify Server class
if(sk.isReadable()){
	//获取连接对象
	SocketChannel sc = (SocketChannel)sk.channel();
	ByteBuffer buf = ByteBuffer.allocate(10);
	sc.read(buf);
	System.out.println("服务器端读取到:"+new String(buf.array()));
	//0000 0101  sk.interestOps()获取原事件
	//1111 1110   !OP_READ
	//0000 0100  OP_WRITE
	//sc.register(sel, SelectionKey.OP_WRITE);
	sc.register(sel, sk.interestOps()&~SelectionKey.OP_READ);
}复制代码

Modify Client class
System.out.println("客户端连入");
ByteBuffer buffer = ByteBuffer.wrap(
"helloworld".getBytes());
sc.write(buffer);
while(true);复制代码

write event

Modify Servet
if(sk.isWritable()){
	//获取SocketChannel
	SocketChannel sc = (SocketChannel)sk.channel();
	ByteBuffer buf = ByteBuffer.wrap("get".getBytes());
	sc.write(buf);
	//去掉写事件
	sc.register(sel, sk.interestOps()&~SelectionKey.OP_WRITE);
}复制代码
Modify Client class
public class NIOClient {
	public static void main(String[] args) throws Exception {
		SocketChannel sc = SocketChannel.open();
		sc.configureBlocking(false);
		sc.connect(new InetSocketAddress("127.0.0.1", 6666));
		while(!sc.isConnected()){
			sc.finishConnect();
		}
		System.out.println("客户端有连接连入");
		ByteBuffer buf = ByteBuffer.wrap(
				"helloworld".getBytes());
		sc.write(buf);
		System.out.println("客户端信息已经写出");
		ByteBuffer readBuf = ByteBuffer.allocate(3);
		sc.read(readBuf);
		System.out.println("客户端读到服务器端传递过来的信息:"
		      +new String(readBuf.array()));
		while(true);
	}
}复制代码
public class Client2 {
public static void main(String[] args) throws IOException {
	SocketChannel sc = SocketChannel.open();
	sc.configureBlocking(false);
	sc.connect(new InetSocketAddress("127.0.0.1", 9999));
	//对于客户端,最开始要注册连接监听
	Selector selector = Selector.open();
	sc.register(selector, SelectionKey.OP_CONNECT);
        while(true){
		selector.select();
		Set<SelectionKey> set = selector.selectedKeys();
		Iterator<SelectionKey> iter = set.iterator();
		while(iter.hasNext()){
			SelectionKey sk = iter.next();
			if(sk.isConnectable()){
			}
			if(sk.isWritable()){
			}
			if(sk.isReadable()){
			}
			iter.remove();
		}
	}
}
}复制代码

Guess you like

Origin juejin.im/post/5d8206f7e51d45620d2cb991