Netty框架(二)————NIO编程

       之前提到的同步阻塞I/O模型存在很多弊端,为了解决这些弊端,我们引进了非阻塞I/O模型,即NIO模型,相比与之前的Serversocket和Socket,NIO提供了ServerSocketChannel和SocketChannel两种套接字通道实现,下面介绍一下NIO的几个关键技术,

NIO工作原理
      
         1、缓冲区(Buffer)
在NIO库中,所有数据都是通过缓冲区处理的,在读取数据时,直接从缓冲区中读取;在写入数据时,写到缓冲区中,缓冲区t通常相当于一个字节数组(ByteBuffer),NIO中所有的数据操作都是在缓冲区中进行。

缓冲区(Buffer)类的继承关系图

每一个Buffer类都实现了Buffer接口,而且每个Buffer类都有一样的操作,除了ByteBuffer,因为大多数的I/O操作都使用ByteBuffer,因此它除了具有一般缓冲区的操作外,还提供了其他特有的操作。

         2、通道Channel
通道就像自来水管一样,网络数据通过Channel进行读写,通道与流的不同之处在于通道是双向的,流只能在一个方向上移动(只能进行读或者写),而通道可以用于读、写,也可以同时用于读写,

和Channel相关的接口及类结构图如下:

从该结构图也可以看到,Channel主要提供的功能如下:

1)当前Channel的状态信息,比如是打开还是关闭等。
2)通过ChannelConfig可以得到的Channel配置信息。
3)Channel所支持的如read、write、bind、connect等IO操作。
4)得到处理该Channel的ChannelPipeline,既而可以调用其做和请求相关的IO操作。

      3、多路复用器Selector

Selector简称选择器,在NIO编程中,它可以选择在通道中已经就绪的任务,简单来讲,Selector会不断轮询注册在其上的Channel,一个Selector可以轮询多个Channel,如果某个Channel上面有新的连接、读或者写事件,这个Channel就会处于就绪状态,然后就会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续I/O操作。

NIO框架图:

服务器端主要步骤:
     步骤一:打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的父管道,代码示例:
ServerSocketChannel acceptorSvr=ServerSocketChannel.open();
   
步骤二:绑定监听端口,设置连接为非阻塞模式,示例代码:

acceptorSvr.socket().bind(new InetSocketAddress(InetAddress.getByName("IP"),port));
		acceptorSvr.configureBlocking(false);



    步骤三:创建Reactor线程,创建多路复用器(selector)并启动线程,代码示例:

       
Selector selector=Selector.open(); new Thread(new ReactorTask()).start();

   步骤四:将ServerSocketChannel注册到Reactor线程的多路复用器上,监听ACCEPT事件,代码示例:

SelectionKey key=acceptorSvr.register(selector, SelectionKey.OP_ACCEPT);


步骤五:多路复用器在线程run方法的无线循环体内轮询就绪的Key,代码示例:

int num=selector.select();
		Set selectedKeys=selector.selectedKeys();
		Iterator it=selectedKeys.iterator();
		while(it.hasNext())
		{
			SelectionKey key=(SelectionKey)it.next();
			//处理I/O事件
		}


步骤六:多路复用器监听到有新的客户端接入,处理新的接入勤奋求请求,完成TCP三次握手,简历建立物理链路,设置客户端链路为非阻塞模式,代码示例:

ServerSocketChannel ssc=(ServerSocketChannel) key.channel();
		SocketChannel channel=ssc.accept();
		channel.configureBlocking(false);
		channel.socket().setReuseAddress(true);


步骤七:将新接入的客户端连接注册到Reactor线程的多路复用器上,监听读操作,用来读取客户端发送的网络消息,代码示例:

channel.register(selector, SelectionKey.OP_READ);

步骤八:异步读取客户端请求消息到缓冲去,代码示例:

ByteBuffer readBuffer=ByteBuffer.allocate(1000);
		int readBytes=channel.read(readBuffer);


步骤九:通过从缓冲区读数据对ByteBuffer进行编解码,将解码后的消息放到业务处理线程池中进行业务逻辑处理,代码示例:

if(readBytes>0)
		{
			//将读指针定位到定位到缓冲区
			readBuffer.flip();
			byte[] bytes=new byte[readBuffer.remaining()];
			readBuffer.get(bytes);
			//获取缓冲区的数据
			String  body=new String (bytes,"UTF-8");
			if(body!=null && !body.isEmpty())
			{
				byte[] bytes=resp.getBytes();
				ByteBuffer writeBuffer=ByteBuffer.allocate(bytes.length);
				//将写指针定位到缓冲区头部
				writeBuffer.flip();
				//将消息写到缓冲区,客户端通过SocketChannel接收到消息
				channel.write(writeBuffer);
			}
		}



服务器主要代码:

package test;

import java.io.IOException;
import java.net.InetAddress;
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;
import java.util.Set;


public class MultiplexerTimeServer implements Runnable{

	private Selector selector;
	private ServerSocketChannel servChannel;
	private volatile boolean stop;
	
	public MultiplexerTimeServer(int port)
	{
		try{
			selector=Selector.open();
			servChannel=ServerSocketChannel.open();
			
			servChannel.configureBlocking(false);
			servChannel.socket().bind(new InetSocketAddress(port),1024);
			servChannel.register(selector,SelectionKey.OP_ACCEPT);
			System.out.println("The TimeServer is Start in port:"+port);
		}
		catch(IOException e){
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public void stop()
	{
		this.stop=true;
	}
	@Override
	public void run()
	{
		while(!stop)
		{
			try{
				selector.select(1000);
				Set<SelectionKey> selectedKeys=selector.selectedKeys();
				Iterator<SelectionKey> it=selectedKeys.iterator();
				SelectionKey key=null;
				while(it.hasNext())
				{
					key=it.next();
					it.remove();
					try{
						handleInput(key);
					}
					catch(Exception e){
						if(key!=null)
						{
							key.cancel();
						if(key.channel()!=null)
							key.channel().close();
						}
					}
				}
			}
			catch(Throwable t)
			{
				t.printStackTrace();
			}
		}
		if(selector!=null)
			try{
				selector.close();
			}
		catch (IOException e) {
			e.printStackTrace();
		}
	}
	private void handleInput(SelectionKey key)throws IOException
	{
		
		if(key.isValid())
		{
			if(key.isAcceptable()){
				ServerSocketChannel ssc=(ServerSocketChannel) key.channel();
				SocketChannel sc=ssc.accept();
				sc.configureBlocking(false);
				sc.register(selector, SelectionKey.OP_READ);
			}
			if(key.isReadable())
			{
				SocketChannel sc=(SocketChannel) key.channel();
				ByteBuffer readBuffer=ByteBuffer.allocate(1000);
				int readBytes=sc.read(readBuffer);
				if(readBytes>0)
				{
					//将读指针定位到定位到缓冲区
					readBuffer.flip();
					byte[] bytes=new byte[readBuffer.remaining()];
					readBuffer.get(bytes);
					//获取缓冲区的数据
					String  body=new String (bytes,"UTF-8");
					System.out.println("The Time server receive order:"+body);
					String currentTime="QUERY TIME ORDER".equalsIgnoreCase(body)? new java.util.Date(System.currentTimeMillis()).toString():"BAD ORDER";
					doWrite(sc,currentTime);
				}
				else if(readBytes<0)
				{
					key.cancel();
					sc.close();
				}
				else
					;
			}
		}
	}
	private void doWrite(SocketChannel channel,String resp) throws IOException
	{
		if(resp!=null && resp.trim().length()>0)
		{
			byte[] bytes=resp.getBytes();
			ByteBuffer writeBuffer=ByteBuffer.allocate(bytes.length);
			writeBuffer.flip();
			channel.write(writeBuffer);
			
		}
	}
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值