xsj工作笔记——JDK NIO学习

BIO/NIO的区别:

1.没有数据缓冲区,只有一个流,I/O性能存在问题;(NIO增加了数据缓冲区)2.没有C/C++的Channel(双管道,同时写入写出,或者一个写入,同时另一个写出)概念,只有输入和输出流(单管道),(NIO改善成了Channel)3.BIO因为是同步的,会导致通信被长时间阻塞(同步非阻塞,异步非阻塞)4.多路复用机制 Selector


BIO通信模型,为什么会很多人诟病:1.4版本之前

Client发送请求到Server,Server中的Acceptor分发给线程进行链路处理,线程输出流返回数据给Client,

当这种1on1的任务不断增多时,会造成线程溢出服务器宕机。

1.5以后出现了NIO(包含1.5)。


NIO核心组成部分:

1.缓冲区buffer对象(NIO做出的重大改变,增大了IO吞吐性能)读写需要转换读写模式 flip();position代表位置变化 

size

position

limit (limit<=size)

传统的IO面向流操作都没有提供Buffer,而NIO是面向Buffer写到Channel

Channel

而且可以同时读写,速度快效率高,使用ServerSocketChannel 服务类配合高速通道Channel()

Selector

多路复用注册器,通过register(select SelectionKey....)注册到selector上

  不断的轮询每个客户端注册到上面的Channel,只要客户端发生读写,就会被Selector轮询到,

通过SelectionKey可以获取这些就绪通道的集合,进行IO操作

通过Select select() 将上述四种状态准备好的通道 返回int值

JDK使用了epoll()取代了传统的select实现,没有最大句柄的1024/2048的限制,

只需要一个线程负责Selector轮询,就可以完成成千上万的客户端接入。

 这篇文章对select、poll、epoll的实现做了详细总结,不再赘述。 

做了一个简单的EchoDemo,代码如下

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.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class NIOServer {
	private static ServerSocketChannel server;
	private static int port=8080;
	//信息写入buffer
	ByteBuffer receiveBuffer=ByteBuffer.allocate(1024);
	//信息写出buffer
	ByteBuffer sendBuffer=ByteBuffer.allocate(1024);
	//多路复用器(注册器)
	private static Selector selector;
	//维护一个实际上是一个事件标签的集合
	Map<SelectionKey, String> sessionMsg=new HashMap<SelectionKey, String>();
	public NIOServer(int port) throws IOException{
		this.port=port;
		server =ServerSocketChannel.open();
		server.socket().bind(new InetSocketAddress(port));
		server.configureBlocking(false);
		selector= Selector.open();
		server.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("NIO Service has started," +
				"and been listening the port:"+this.port);
	}
	//监听请求方法模块
	public void listener() throws IOException{
		while(true){
			//到这里就是通过selector来看有没有注册事件
			int events=selector.select();
			if(events<=0){
				//继续轮询
				//NIO的内部机制就是不断轮询注册到selector的多个channel
				continue;
			}
			//有事件
			Set<SelectionKey> keys=selector.selectedKeys();
			Iterator<SelectionKey> iterator=keys.iterator();
			while(iterator.hasNext()){
				//处理客户端事件
				process(iterator.next());
				//移除集合里面的事件
				iterator.remove();
			}
		}
	}
	//详细的处理客户端事件
	private void process(SelectionKey key) {
		SocketChannel clients=null;
		try {
		if(key.isValid() && key.isAcceptable()){
			clients=server.accept();
			clients.configureBlocking(false);
			clients.register(selector, SelectionKey.OP_READ);
			//读取客户端消息
		}else if(key.isValid() && key.isReadable()){
			//读到缓冲区
			receiveBuffer.clear();
			clients=(SocketChannel)key.channel();
			int len=clients.read(receiveBuffer);
			if(len>0){
				String msg=new String (receiveBuffer.array(),0,len);
				sessionMsg.put(key, msg);
				System.out.println("收到从客户端发送的消息:"+msg);
				//告诉我们的复用器,下次可以写数据
				clients.register(selector,SelectionKey.OP_WRITE);
				
				}
			}else if(key.isValid() && key.isWritable()){
				if(!sessionMsg.containsKey(key)){
					return;
				}
				//服务会客户端消息
				clients=(SocketChannel)key.channel();
				sendBuffer.clear();
				sendBuffer.put(new String(sessionMsg.get(key)+" request has already processed").getBytes());
				//这个实际上设置读取位
				sendBuffer.flip();
				clients.write(sendBuffer);
				clients.register(selector, SelectionKey.OP_READ);
			}
		} catch (IOException e) {
			//读取key的事件的时候	遇到客户端异常下线 不会引起异常
			try {
				key.cancel();
				clients.socket().close();
				clients.close();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
		
	}
	
	public static void main(String[] args){
		try {
			new NIOServer(port).listener();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


 

package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class NIOClient {
	SocketChannel client=null;
	InetSocketAddress serverAddress=new InetSocketAddress("localhost", 8080);
	Selector selector=null;
	ByteBuffer receiveBuffer=ByteBuffer.allocate(1024);
	ByteBuffer sendBuffer=ByteBuffer.allocate(1024);
	
	public NIOClient() throws IOException{
		//修路
		client=SocketChannel.open();
		client.configureBlocking(false);
		client.connect(serverAddress);
		selector=Selector.open();
		//向selector注册链接事件
		client.register(selector, SelectionKey.OP_CONNECT);
	}
	
	public void session() throws IOException{
		if(client.isConnectionPending()){
			client.finishConnect();
			client.register(selector, SelectionKey.OP_WRITE);
			System.out.println("Already connected to the server,please write some message...");
		}
		Scanner scan=new Scanner(System.in);
		while (scan.hasNextLine()) {
			String name=scan.nextLine();
			if("".equals(name)){
				continue;
			}
			if("finish".equals(name)){
				System.exit(0);
			}
			process(name);
		}
	}
	
	private void process(String name) throws IOException{
		boolean waitHelp=true;
		Iterator<SelectionKey> iterator =null;
		Set<SelectionKey> keys=null;
//		try{
		try {
			while(waitHelp){
				//如果没有继续轮询,和read()类似
				int readys=selector.select();
				if(readys == 0){
					continue;
				}
				//得到SelectionKey并迭代一下
				keys=selector.selectedKeys();
				iterator=keys.iterator();
				while(iterator.hasNext()){
					SelectionKey key=iterator.next();
					//首先要判断是否有效和可写,代表Client想Server发送信息
					if(key.isValid() && key.isWritable()){
						sendBuffer.clear();
						sendBuffer.put(name.getBytes());
//					在一系列通道读取或放置 操作之后,调用此方法为一系列通道写入或相对获取 操作做好准备。例如: jdk1.6
						sendBuffer.flip();
						client.write(sendBuffer);
						client.register(selector, SelectionKey.OP_READ);
					}else if(key.isValid() && key.isReadable()){
						receiveBuffer.clear();
						int len=client.read(receiveBuffer);
						if(len>0){
							receiveBuffer.flip();
							System.out.println("服务器反馈的消息:"+new String(receiveBuffer.array(),0,len));
							client.register(selector, SelectionKey.OP_WRITE);
							waitHelp=false;
						}
					}
					//检查完之后,client走开
					iterator.remove();
				}
				
			}
		} catch(IOException e){
			((SelectionKey)keys).cancel();
			client.socket().close();
			client.close();
			return;
		}
	}
    /** 
     * ByteBuffer 转换 String 
     * @param buffer 
     * @return 
     */  
    public static String getString(ByteBuffer buffer)  
    {  
        Charset charset = null;  
        CharsetDecoder decoder = null;  
        CharBuffer charBuffer = null;  
        try  
        {  
            charset = Charset.forName("UTF-8");  
            decoder = charset.newDecoder();  
            // charBuffer = decoder.decode(buffer);//用这个的话,只能输出来一次结果,第二次显示为空  
            charBuffer = decoder.decode(buffer.asReadOnlyBuffer());  
            return charBuffer.toString();  
        }  
        catch (Exception ex)  
        {  
            ex.printStackTrace();  
            return "";  
        }  
    } 
	public static void main(String[] args) throws IOException {
		new NIOClient().session();
	}
}
	


  

   




猜你喜欢

转载自blog.csdn.net/qq_18494159/article/details/79087484