Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
Netty基于java的NIO核心:缓存区、通道、选择器。这里推荐一本书:java NIO,出版社是O'REILLY。
一、缓存区。
缓冲区的工作与通道紧密联系,通道是I/O传输发生时通过的入口,而缓冲区是这些数据传输的来源或目标。对于离开缓冲区的传输,你想传递出去的数据被置于一个缓冲区,被传送到通道。对于传回缓冲区的传输,一个通道将数据放置在你所提供的缓冲区中。这种在协同对象之间进行的缓冲区数据传递是高效数据处理的关键。
缓冲区的属性:容量(capacity能够容纳的数据元素的最大数量)、上界(limit现存元素的计数)、标记(mark一个备忘位置)、位置(Posistion下一个要被读或写的元素的索引),还有缓冲区操作:翻转、压缩等。
这里只简介那个位置指针就好了,一切从它开始。
初始状态:一个空的字节缓冲区,position指向0
接着我们写入一些数据,例如:
byteBuffer.put(110);
byteBuffer.put(120);
现在的状态是:posistion指向2了,如果再写入数据,那么就会在posistion(这里是2)的位置继续向上存储了。如果你把指针设置为1,那么数据就会从1位置开始写入,原先的数据会被覆盖
如果你需要把数据读出来,那么你需要把指针重置0,然后调用get()方法获取第一个字节,这时候posistion会自动+1,指向1,如此类推直到把全部数据读完。
示例:
1 public static void main(String[] args) { 2 ByteBuffer buffer = ByteBuffer.allocate(10); 3 buffer.put((byte) 110);//posistion->1 4 buffer.put((byte) 120);//posistion->2 5 6 //获取posistion为2的数据,即缓存区第三个元素。 7 System.out.println("byteBuffer get:"+buffer.get()); 8 //翻转,使posistion指向0 9 buffer.flip(); 10 11 //获取posistion为0的数据,即缓存区第一个个元素。此时posistion指向1 12 System.out.println("byteBuffer get:"+buffer.get()); 13 //获取posistion为1的数据,即缓存区第二个个元素。此时posistion指向2 14 System.out.println("byteBuffer get:"+buffer.get()); 15 16 //或者可以直接指定下标 17 System.out.println("byteBuffer get(0):"+buffer.get(0)); 18 System.out.println("byteBuffer get(1):"+buffer.get(1)); 19 20 }
输出结果:
byteBuffer get:0
byteBuffer get:110
byteBuffer get:120
byteBuffer get(0):110
byteBuffer get(1):120
二、通道。
Channel用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据。通道是一种途径,借助该途径,可以用最小的总开销来访问操作系统本身的I/O服务。缓冲区则是通道内部用来发送和接受数据的端点。
我们在网络编程中主要用到四个通道:文件通道FileChannel、套接字通道ServerSocketChannel/SocketChannel/DatagramChannel。其中FileChannel和DatagramChannel不在讨论范围。
要想使用ServerSocketChannel和SocketChannel,首先要打开通道,然后绑定端口,连接成功后就可以对Buffer进行读和写的操作了(记住,ServerSocket/Socket是通过inputStream和outputStream对数据进行操作的,而通道的操作对象是缓冲区):
1 public static void main(String[] args) { 2 int serverPort = 8001; 3 ServerSocketChannel serverChannel; 4 try { 5 //打开通道 6 serverChannel = ServerSocketChannel.open(); 7 //ServerSocketChannel本身不提供绑定功能,只能调用它所包装的ServerSocket的方法 8 serverChannel.socket().bind(new InetSocketAddress(serverPort)); 9 } catch (IOException e) { 10 } 11 12 try { 13 SocketChannel clientChannel = SocketChannel.open(); 14 clientChannel.connect(new InetSocketAddress("192.168.21.94",serverPort)); 15 } catch (IOException e) { 16 } 17 18 }
他们继承了WritableByteChannel和ReadableByteChannel接口,从而有了读和写的可能:
1 public interface WritableByteChannel{ 2 public int write(ByteBuffer src) throws IOException; 3 } 4 5 public interface ReadableByteChannel{ 6 public int read(ByteBuffer dst) throws IOException; 7 }
下面展示一个简单的示例:
1 public class ServerChannelTest { 2 3 public static void main(String[] args) { 4 int serverPort = 8001; 5 ServerSocketChannel serverChannel; 6 try { 7 //打开通道 8 serverChannel = ServerSocketChannel.open(); 9 //ServerSocketChannel本身不提供绑定功能,只能调用它所包装的ServerSocket的方法 10 serverChannel.socket().bind(new InetSocketAddress(serverPort)); 11 System.out.println("wait from client……"); 12 SocketChannel accept = serverChannel.accept(); 13 ByteBuffer buffer = ByteBuffer.allocate(10); 14 accept.read(buffer); 15 buffer.flip(); 16 System.out.println("rec from client:"+buffer.get()); 17 } catch (IOException e) { 18 e.printStackTrace(); 19 } 20 } 21 22 } 23 24 25 26 public class ClientChannelTest { 27 28 public static void main(String[] args) { 29 int serverPort = 8001; 30 try { 31 SocketChannel clientChannel = SocketChannel.open(); 32 clientChannel.connect(new InetSocketAddress("127.0.0.1",serverPort)); 33 ByteBuffer buffer = ByteBuffer.allocate(10); 34 buffer.put((byte) 1); 35 buffer.flip(); 36 clientChannel.write(buffer); 37 } catch (IOException e) { 38 e.printStackTrace(); 39 } 40 41 } 42 43 }