netty入门学习(1)-从NIO说起
2013-05-29 16:02
387 查看
学习netty之前,先来一个NIO的入门级示例(当然netty不仅仅对NIO进行了封装支持,对BIO同样也支持,本系统只针对NIO进行处理):
一:
服务端
二:
客户端
在这个示例中有几点需要注意:
(1)服务端的写事件什么时候触发?服务在对accept事件进行处理时,将写事件注册到注册集合,JVM会根据服务端的TCP缓冲区(这个是内核态的,区别于ByteBuffer等用户态缓冲区)是否可写入(未满)来决定是否将写事件加入触发集合,即触发写操作。注意:应根据业务场景来决定是否注销写事件,如果不注销,很容易造成CPU空转。
当然也可以将触发写操作的业务代码写到对accpet事件的处理中,如下。但是这样做有可能因TCP缓冲区已满,而引起写阻塞,违背了NIO的初忠。
(2)在NIO模型中,服务端一次写操作,有可能会导致客户端多次读操作。同样,服务端多次写操作,也可能只会触发客户端一次读操作。所以在具体的业务操作编程时,需要对此种情况进行处理。
一:
服务端
import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; 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 ChargenServer { public static int DEFAULT_PORT = 19; public static void main(String[] args){ int port; try{ port = Integer.parseInt(args[0]); }catch(Exception ex){ port = DEFAULT_PORT; } System.out.println("Listening for connections on port " + port); byte[] rotation = new byte[95*2]; for(byte i=' ';i<='~';i++){ rotation[i-' '] = i; rotation[i+95-' '] = i; } ServerSocketChannel serverChannel; Selector selector; try{ serverChannel = ServerSocketChannel.open(); ServerSocket ss = serverChannel.socket(); InetSocketAddress address = new InetSocketAddress(port); ss.bind(address); serverChannel.configureBlocking(false); selector = Selector.open(); serverChannel.register(selector, SelectionKey.OP_ACCEPT); }catch(IOException ex){ ex.printStackTrace(); return; } while(true){ try{ selector.select(); }catch(IOException ex){ ex.printStackTrace(); break; } Set readyKeys = selector.selectedKeys(); Iterator iterator = readyKeys.iterator(); while(iterator.hasNext()){ SelectionKey key = (SelectionKey)iterator.next(); iterator.remove(); try{ if(key.isAcceptable()){ ServerSocketChannel server = (ServerSocketChannel)key.channel(); SocketChannel client = server.accept(); System.out.println("Accepted connection from " + client); client.configureBlocking(false); SelectionKey key2 = client.register(selector, SelectionKey.OP_WRITE); ByteBuffer buffer = ByteBuffer.allocate(74); buffer.put(rotation,0,72); buffer.put((byte)'\r'); buffer.put((byte)'\n'); buffer.flip(); key2.attach(buffer); }else if(key.isWritable()){//测试此键的通道是否已准备好进行写入 SocketChannel client = (SocketChannel)key.channel(); ByteBuffer buffer = (ByteBuffer)key.attachment(); if(!buffer.hasRemaining()){ buffer.rewind(); int first = buffer.get(); buffer.rewind(); int position = first - ' ' + 1; buffer.put(rotation,position,72); buffer.put((byte)'\r'); buffer.put((byte)'\n'); buffer.flip(); } client.write(buffer); } } catch(IOException ex){ key.cancel(); try{ key.channel().close(); }catch(IOException cex){} } } } } }
二:
客户端
import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.SocketChannel; import java.nio.channels.WritableByteChannel; public class ChargenClient { public static int DEFAULT_PORT = 19; public static void main(String[] args){ try{ SocketAddress address = new InetSocketAddress(DEFAULT_PORT); SocketChannel client = SocketChannel.open(address); ByteBuffer buffer=ByteBuffer.allocate(74); WritableByteChannel out = Channels.newChannel(System.out); while(client.read(buffer) != -1){ buffer.flip(); out.write(buffer); buffer.clear(); } }catch(IOException ex){ ex.printStackTrace(); } } }
在这个示例中有几点需要注意:
(1)服务端的写事件什么时候触发?服务在对accept事件进行处理时,将写事件注册到注册集合,JVM会根据服务端的TCP缓冲区(这个是内核态的,区别于ByteBuffer等用户态缓冲区)是否可写入(未满)来决定是否将写事件加入触发集合,即触发写操作。注意:应根据业务场景来决定是否注销写事件,如果不注销,很容易造成CPU空转。
当然也可以将触发写操作的业务代码写到对accpet事件的处理中,如下。但是这样做有可能因TCP缓冲区已满,而引起写阻塞,违背了NIO的初忠。
public static void main(String[] args){ int port; try{ port = Integer.parseInt(args[0]); }catch(Exception ex){ port = DEFAULT_PORT; } System.out.println("Listening for connections on port " + port); byte[] rotation = new byte[95*2]; for(byte i=' ';i<='~';i++){ rotation[i-' '] = i; rotation[i+95-' '] = i; } ServerSocketChannel serverChannel; Selector selector; try{ serverChannel = ServerSocketChannel.open(); ServerSocket ss = serverChannel.socket(); InetSocketAddress address = new InetSocketAddress(port); ss.bind(address); serverChannel.configureBlocking(false); selector = Selector.open(); serverChannel.register(selector, SelectionKey.OP_ACCEPT); }catch(IOException ex){ ex.printStackTrace(); return; } while(true){ try{ selector.select(); }catch(IOException ex){ ex.printStackTrace(); break; } Set readyKeys = selector.selectedKeys(); Iterator iterator = readyKeys.iterator(); while(iterator.hasNext()){ SelectionKey key = (SelectionKey)iterator.next(); iterator.remove(); try{ if(key.isAcceptable()){ ServerSocketChannel server = (ServerSocketChannel)key.channel(); SocketChannel client = server.accept(); System.out.println("Accepted connection from " + client); client.configureBlocking(false); ByteBuffer buffer = ByteBuffer.allocate(74); buffer.put(rotation,0,72); buffer.put((byte)'\r'); buffer.put((byte)'\n'); buffer.flip(); client.write(buffer); } } catch(IOException ex){ key.cancel(); try{ key.channel().close(); }catch(IOException cex){} } } } }
(2)在NIO模型中,服务端一次写操作,有可能会导致客户端多次读操作。同样,服务端多次写操作,也可能只会触发客户端一次读操作。所以在具体的业务操作编程时,需要对此种情况进行处理。
相关文章推荐
- nio学习之netty入门(3)---发送对象
- nio学习之netty入门(1)---发送字符串
- Netty学习之NIO入门
- nio学习之netty入门(2)---netty中handler的执行顺序
- NIO-netty-入门学习
- Netty系列-NIO入门
- Netty学习手册(一、基本使用入门)
- netty深入学习之中的一个: 入门篇
- netty学习十一:NIO客户端服务端通讯demo
- 学习 java netty (一) -- java nio
- Netty入门学习
- 学习 java netty (一) -- java nio
- netty入门学习
- netty学习之ChannelSink(NioClientSocketPipelineSink)
- Netty学习之旅------线程模型前置篇Reactor反应堆设计模式实现(基于java.nio)
- NIO通讯框架之阿堂教程:Mina学习笔记-入门篇(一)
- 【Netty】netty学习之nio网络编程的模型
- netty学习之一:java.nio.ByteBuffer
- Netty源码学习-Java-NIO-Reactor
- netty入门学习(4)-LengthFieldPrepender和LengthFieldBasedFrameDecoder