Java游戏服务器系列:传统的BIO(OIO)、NIO与Netty中的BIO(OIO)、NIO

5 篇文章 0 订阅
2 篇文章 0 订阅
BIO 、NIO 、OIO
  • BIO就是OIO,BIO是阻塞IO模型(Block-I/O)
  • NIO是非阻塞IO模型(Non-Block I/O),
  • 有人将NIO称作(New-I/O),所以也将BIO称之为(Old-I/O),简称OIO
  • 下文中开始称之为OIO,因为Netty使用了这一简称,为了不对代码造成混淆,所以也是用OIO的命名方式
jdk-API中的OIO
  • 定义:网络链接进入时,直接阻塞线程进行I/O读取与写入,链接关闭之前,这个线程都不会做其他工作。有比较详细的可参读作者的另外一篇博文OIO介绍
  • 这里就直接使用多线程的版本代码:
public class JDK_OIOServer{
	public void start(int port) throws IOException {
		// 初始化ServerSocket 并且绑定端口
		final ServerSocket serverSocket = new ServerSocket(port);
		for(;;){
			//获取远程客户端的链接
			final Socket clientSocket = serverSocket.accept();
			//启用线程
			new Thread(new Runnable(){
				@override 
				public void run(){
					try{
						//获得写输出流
						OutputStream out= client.getOutputStream();
						//写入字符并设置字符集
						out.wirte("Hello",getBytes(Charset.forName("UTF-8")));
						//冲刷数据
						out.flush();
					}
					catch (IOException e){
						e.printStackTrace();	
					}
					finally{
						//关闭客户端链接
						try{
							clientSocket.close();
						}catch(IOException e){
							e.printStackTrace();	
						}
					}
				}
			}).start();
		}
	}
}
jdk-API中的NIO
  • 定义:核心为一个选择器,将所有链接和服务器监听对象都注册进这个选择器中,监听这个选择器是否有事件发生,如果有事件发生则进行动作,理论上是一个事件驱动的模型,但是同时还是阻塞的。详细可见作者的另外一篇博客NIO
  • 代码:
public class JDK_NIOServer{
	public void start(int port){
		//得到serverChannel对象
		ServerSocketChannel serverChannel = ServerSocketChannel.open();
		//配置为非阻塞状态
		serverChannel.configureBlocking(false);
		//得到选择器
		Selector selector = Selector.open();
		//得到serverChannel中的serverSocket,网络链接真实发生的地方就是serverSocket
		ServerSocket serverSocket = serverChannel.channel();
		//初始化地址和端口,这里使用了本地回环地址
		InetSocketAddress address = new InetSocketAddress(port);
		//serverSocket绑定地址
		serverSocket.bind(address);
		//最后将serverChannel注册进选择器,并且监听OP_ACCEPT事件,也就是网络链接发生事件
		serverChannel.register(selector,SelectionKey.OP_ACCEPT);
		for(;;){
			try{
				//阻塞自己,监听事件发生,
				//select()方法也有一个参数,可以接受时间,这样当前线程不会被完全阻塞
				//加时间参数的方法适用于不需要及时响应的服务器环境
				selector.select();
			}catch(IOException e){
				e.printStackTrace();
				break;
			}
			//获取到选择器中发生时间的key的集合
			Set<SelectionKey> keys = selector.selectedKeys();
			//获取key集合的迭代器
			Iterator<SelectionKey> iterators = keys.iterator();
			while(iterators.hasNext()){
				//获取key
				SelectKey key = iterators.next();
				try{
					//如果是链接进入事件
					if(key.isAcceptable()){
						//这里这么做是因为可能有多个ServerSocketChannel同时工作在一个选择器
						//key中包含了很多信息
						ServerSocketChannel sChanel =(ServerSocketChannel) key.channel();
						//调用ServerSocketChannel的accept方法获取当前客户端链接Channel
						SocketChannel cChanel = sChannel.accept();
						//配置为非阻塞
						cChannel.configureBlocking(false);
						//将channel注册进选择器,并且分配一个1024字节的缓冲区
						cChannel.register(selector,SelectionKey.OP_READ | SelectionKey.OP_WRITE,BufferByte.allocate(1024));
					}
					//写事件发生
					if(key.isWriteable()){
						//获得到当前的channel,注意,读事件写事件的channel都是客户端链接Channel,
						SocketChannel cChannel = (SocketChannel)key.channel();
						//获得到链接的字符缓冲
						ByteBuffer buffer = (ByteBuffer) key.attachment();
						while(buffer.hasRemaining()){
							if(cChannel.write(buffer)==0){
								break;
							}
						}
						//关闭channel
						cChannel.close();
					}
				}catch(IOException e ){
					key.cancel();
					try{
						key.channel.close();
					}catch(IOException e){
						e.printStackTrace();
					}
				}finally{
					//最终移除掉这个key,防止重复操作
					iterators.remove();
				}
			}
		}
	}
}
Netty中的OIO
  • 直接上代码分析吧,尽量每一行代码都注释,但是如果不了解Netty工作流程的人可能看起来会有一些难度
public class Netty_OIOServer{
	public void start(int port){
		//创建内容为Hi的字节缓冲流
		final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n",Charset.forName("UTF-8")));
		//创建一个EventLoopGroup组
		EventLoopGroup evtLoopGroup = new OioEventLoopGroup();
		try{
			//bootStrap是netty服务器管理的核心组件
			ServerBootStrap bootStrap = new ServerBootStrap();
			
			bootStrap.group(evtLoopGroup)//添加组
					 //使用什么ServerSocketChannel作为服务器接受链接事件的通道
					 .channel(OioServerSocketChannel.class)
					 //绑定地址
					 .localAddress(new InetSocketAddress(port))
					 //添加回调函数,使用ChannelInitialize初始化
					 //其实就是当有链接进入时,回用什么进行操作
					 .childHandler(new ChannelInitialize<SocketChannel>(){
					 	//链接进入时回创建一个SocketChannel
					 	//此时调用ChannelInitialize的initChannel进行初始化
					 	@override
					 	public void initChannel(SocketChannel ch) throws Exception {
					 		//获得SocketChannel,并且对SocketChannel的pipeline添加回调函数
					 		//继承自ChannelInboundHandler,或者ChannelOutboundHandler
					 		 ch.pipeline.addLast(new ChannelInboundHandler{
					 		 	@override 
					 		 	public void channelActive(ChannelHandlerContext ctx) throws Exception {
					 		 		ctx.writeAndFlush(buf.duplicate())
					 		 		   .addListener(ChannelFutureListen.CLOSE); 
					 		 	} 	
					 		 })
					 	} 
					 }) 
		}
		
	}
}
Netty中的NIO
public class Netty_OIOServer{
	public void start(int port){
		//创建内容为Hi的字节缓冲流
		final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n",Charset.forName("UTF-8")));
		//创建一个EventLoopGroup组
		EventLoopGroup evtLoopGroup = new NioEventLoopGroup();
		try{
			//bootStrap是netty服务器管理的核心组件
			ServerBootStrap bootStrap = new ServerBootStrap();
			
			bootStrap.group(evtLoopGroup)//添加组
					 //使用什么ServerSocketChannel作为服务器接受链接事件的通道
					 .channel(NioServerSocketChannel.class)
					 //绑定地址
					 .localAddress(new InetSocketAddress(port))
					 //添加回调函数,使用ChannelInitialize初始化
					 //其实就是当有链接进入时,回用什么进行操作
					 .childHandler(new ChannelInitialize<SocketChannel>(){
					 	//链接进入时回创建一个SocketChannel
					 	//此时调用ChannelInitialize的initChannel进行初始化
					 	@override
					 	public void initChannel(SocketChannel ch) throws Exception {
					 		//获得SocketChannel,并且对SocketChannel的pipeline添加回调函数
					 		//继承自ChannelInboundHandler,或者ChannelOutboundHandler
					 		 ch.pipeline.addLast(new ChannelInboundHandler{
					 		 	@override 
					 		 	public void channelActive(ChannelHandlerContext ctx) throws Exception {
					 		 		ctx.writeAndFlush(buf.duplicate())
					 		 		   .addListener(ChannelFutureListen.CLOSE); 
					 		 	} 	
					 		 })
					 	} 
					 }) 
		}
		
	}
}
总结

这里没有详细的将Netty的工作流程说的很详细,但是大家可以做一个小对比

  • Netty相对于传统的jdk-API实现来说,更加的模块化,我们仅需要在不同的阶段添加我们的处理逻辑就可以了,而不需要实现整个通信流程,然后在通信流程的不同阶段实现我们的逻辑
  • Netty的OIO、NIO实现几乎一摸一样,说明Netty本身抽象程度足够高,我们仅需要知道Netty的流程就可以,而不需要知道OIO模型或者NIO模型的不同之处
  • 上面两点已经足够减少我们对于项目模型的构建复杂程度,而只需要专心于服务器逻辑的实现就好
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值