本文共 10488 字,大约阅读时间需要 34 分钟。
转:https://blog.csdn.net/qq_18860653/article/details/53406723
Netty的使用或许我们看着官网user guide还是很容易入门的。因为java nio使用非常的繁琐,netty对java nio进行了大量的封装。对于Netty的理解,我们首先需要了解NIO的原理和使用。所以,我也特别渴望去了解NIO这种通信模式。
官方的定义是:nio 是non-blocking的简称,在jdk1.4 里提供的新api 。Sun 官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。 Channel :一个新的原始I/O 抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O 。是不是很抽象?
在阅读这篇技术文档之后,收获了很多。包括对Java NIO的理解和使用,所以也特别的感谢作者。
首先,还是来回顾以下从这篇文档中学到的要点。
为什么要使用 NIO?NIO 的创建目的是为了让 Java 程序员可以实现高速 I/O 而无需编写自定义的本机代码。NIO 将最耗时的 I/O 操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。
NIO最重要的组成部分
通道 Channels缓冲区 Buffers选择器 Selectors
Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。
在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,您都是将它放到缓冲区中。缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不 仅仅 是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。
Channel是一个对象,可以通过它读取和写入数据
看完下面这个例子,基本上就理解buffer和channel的作用了
-
package yyf.java.nio.ibm;
-
-
-
-
import java.nio.channels.*;
-
-
-
static public void main(String args[]) throws Exception {
-
-
String infile =
"c://test/nio_copy.txt";
-
String outfile =
"c://test/result.txt";
-
-
FileInputStream fin =
new FileInputStream(infile);
-
FileOutputStream fout =
new FileOutputStream(outfile);
-
-
FileChannel fcin = fin.getChannel();
-
-
FileChannel fcout = fout.getChannel();
-
-
ByteBuffer buffer = ByteBuffer.allocate(
1024);
-
-
-
-
-
-
int r = fcin.read(buffer);
-
-
-
-
-
-
-
-
-
-
-
缓冲区主要是三个变量 positionlimitcapacity这三个变量一起可以跟踪缓冲区的状态和它所包含的数据。我们将在下面的小节中详细分析每一个变量,还要介绍它们如何适应典型的读/写(输入/输出)进程。在这个例子中,我们假定要将数据从一个输入通道拷贝到一个输出通道。Position您可以回想一下,缓冲区实际上就是美化了的数组。在从通道读取时,您将所读取的数据放到底层的数组中。 position 变量跟踪已经写了多少数据。更准确地说,它指定了下一个字节将放到数组的哪一个元素中。因此,如果您从通道中读三个字节到缓冲区中,那么缓冲区的 position 将会设置为3,指向数组中第四个元素。同样,在写入通道时,您是从缓冲区中获取数据。 position 值跟踪从缓冲区中获取了多少数据。更准确地说,它指定下一个字节来自数组的哪一个元素。因此如果从缓冲区写了5个字节到通道中,那么缓冲区的 position 将被设置为5,指向数组的第六个元素。Limitlimit 变量表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。position 总是小于或者等于 limit。Capacity缓冲区的 capacity 表明可以储存在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小 ― 或者至少是指定了准许我们使用的底层数组的容量。limit 决不能大于 capacity。
缓冲区作为一个数组,这三个变量就是其中数据的标记,也很好理解。
Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。
接下来来看看具体的使用把,我创建了一个直接收消息的服务器(一边接收一边写数据可能对于新手不好理解)
服务端:
-
package yyf.java.nio.test;
-
-
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;
-
-
-
public class NioReceiver {
-
@SuppressWarnings(
"null")
-
public static void main(String[] args) throws Exception {
-
ByteBuffer echoBuffer = ByteBuffer.allocate(
8);
-
ServerSocketChannel ssc = ServerSocketChannel.open();
-
Selector selector = Selector.open();
-
ssc.configureBlocking(
false);
-
ServerSocket ss = ssc.socket();
-
InetSocketAddress address =
new InetSocketAddress(
8080);
-
-
SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
-
System.out.println(
"开始监听……");
-
-
int num = selector.select();
-
Set selectedKeys = selector.selectedKeys();
-
Iterator it = selectedKeys.iterator();
-
-
SelectionKey sKey = (SelectionKey) it.next();
-
SocketChannel channel =
null;
-
if (sKey.isAcceptable()) {
-
ServerSocketChannel sc = (ServerSocketChannel) key.channel();
-
-
channel.configureBlocking(
false);
-
channel.register(selector, SelectionKey.OP_READ);
-
-
}
else if (sKey.isReadable()) {
-
channel = (SocketChannel) sKey.channel();
-
-
-
int r = channel.read(echoBuffer);
-
-
-
System.out.println(
"接收完毕,断开连接");
-
-
-
System.out.println(
"##" + r +
" " +
new String(echoBuffer.array(),
0, echoBuffer.position()));
-
-
-
-
-
-
-
-
-
-
-
-
客户端(NIO): -
package yyf.java.nio.test;
-
-
import java.net.InetSocketAddress;
-
import java.nio.ByteBuffer;
-
import java.nio.channels.SelectionKey;
-
import java.nio.channels.Selector;
-
import java.nio.channels.SocketChannel;
-
import java.util.Iterator;
-
-
-
-
public static void main(String[] args) throws Exception {
-
ByteBuffer echoBuffer = ByteBuffer.allocate(
1024);
-
SocketChannel channel =
null;
-
Selector selector =
null;
-
channel = SocketChannel.open();
-
channel.configureBlocking(
false);
-
-
channel.connect(
new InetSocketAddress(
"localhost",
8080));
-
selector = Selector.open();
-
channel.register(selector, SelectionKey.OP_CONNECT);
-
int num = selector.select();
-
Set selectedKeys = selector.selectedKeys();
-
Iterator it = selectedKeys.iterator();
-
-
SelectionKey key = (SelectionKey) it.next();
-
-
if (key.isConnectable()) {
-
if (channel.isConnectionPending()) {
-
if (channel.finishConnect()) {
-
-
key.interestOps(SelectionKey.OP_READ);
-
echoBuffer.put(
"123456789abcdefghijklmnopq".getBytes());
-
-
System.out.println(
"##" +
new String(echoBuffer.array()));
-
channel.write(echoBuffer);
-
System.out.println(
"写入完毕");
-
-
-
-
-
-
-
-
-
运行结果:
-
-
-
-
-
-
当然,BIO的客户端也可以,开启10个BIO客户端线程
-
package yyf.java.nio.test;
-
-
import java.io.ByteArrayOutputStream;
-
import java.io.IOException;
-
import java.io.InputStream;
-
import java.io.OutputStream;
-
import java.net.InetAddress;
-
import java.net.InetSocketAddress;
-
-
import java.net.UnknownHostException;
-
-
-
import yyf.java.test.Main;
-
-
public class BioClientTest {
-
public static void main(String[] args) throws Exception {
-
BioClient n =
new BioClient();
-
for (
int i =
0; i <
10; i++) {
-
Thread t1 =
new Thread(n);
-
-
-
-
-
-
class BioClient implements Runnable {
-
-
-
-
-
Socket socket =
new Socket(
"127.0.0.1",
8080);
-
OutputStream os = socket.getOutputStream();
-
InputStream is = socket.getInputStream();
-
ByteArrayOutputStream bos =
new ByteArrayOutputStream();
-
String str = Thread.currentThread().getName() +
"...........sadsadasJava";
-
os.write(str.getBytes());
-
StringBuffer sb =
new StringBuffer();
-
byte[] b =
new byte[
1024];
-
-
while ((len = is.read(b)) != -
1) {
-
-
-
-
-
-
System.out.println(Thread.currentThread().getName() +
" 写入完毕 " +
new String(bos.toByteArray()));
-
-
-
-
-
-
运行结果:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
当然,这只是一个测试,对于一个服务器,是有读取,也有写出的,这是文档给的一个服务端例子
-
package yyf.java.nio.ibm;
-
-
-
-
-
import java.nio.channels.*;
-
-
-
public class MultiPortEcho {
-
-
private ByteBuffer echoBuffer = ByteBuffer.allocate(
5);
-
-
public MultiPortEcho(int ports[]) throws IOException {
-
-
-
-
-
private void go() throws IOException {
-
Selector selector = Selector.open();
-
for (
int i =
0; i < ports.length; ++i) {
-
ServerSocketChannel ssc = ServerSocketChannel.open();
-
ssc.configureBlocking(
false);
-
ServerSocket ss = ssc.socket();
-
InetSocketAddress address =
new InetSocketAddress(ports[i]);
-
-
SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
-
System.out.println(
"Going to listen on " + ports[i]);
-
-
-
-
int num = selector.select();
-
Set selectedKeys = selector.selectedKeys();
-
Iterator it = selectedKeys.iterator();
-
-
SelectionKey key = (SelectionKey) it.next();
-
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
-
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
-
SocketChannel sc = ssc.accept();
-
sc.configureBlocking(
false);
-
SelectionKey newKey = sc.register(selector, SelectionKey.OP_READ);
-
-
System.out.println(
"Got connection from " + sc);
-
}
else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
-
SocketChannel sc = (SocketChannel) key.channel();
-
-
-
-
int r = sc.read(echoBuffer);
-
-
-
-
-
-
-
-
-
System.out.println(
"Echoed " + bytesEchoed +
" from " + sc);
-
-
-
-
-
-
-
-
-
-
-
static public void main(String args[]) throws Exception {
-
int ports[] =
new int[] {
8080 };
-
for (
int i =
0; i < args.length; ++i) {
-
ports[i] = Integer.parseInt(args[i]);
-
-
new MultiPortEcho(ports);
-
-
现在,我们就写个客户端去跟服务器通信,把发过去的返回来:
-
-
-
import java.io.IOException;
-
import java.net.InetSocketAddress;
-
import java.net.SocketAddress;
-
import java.nio.ByteBuffer;
-
import java.nio.channels.SocketChannel;
-
-
import javax.swing.ButtonGroup;
-
-
-
public static void main(String[] args) throws IOException {
-
SocketChannel socketChannel = SocketChannel.open();
-
SocketAddress socketAddress =
new InetSocketAddress(
"127.0.0.1",
8080);
-
socketChannel.connect(socketAddress);
-
-
ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
-
socketChannel.write(buffer);
-
socketChannel.socket().shutdownOutput();
-
-
-
-
-
while ((count = socketChannel.read(buffer)) >
0) {
-
-
-
-
System.out.println(
new String(buffer.array()));
-
-
-
socketChannel.socket().shutdownInput();
-
socketChannel.socket().close();
-
-
-
运行结果 server:
-
-
Got connection from java.nio.channels.SocketChannel[connected local=/
127.0.0.1:
8080 remote=/
127.0.0.1:
63584]
-
Echoed
7 from java.nio.channels.SocketChannel[closed]
client:
你好a
对于NIO的入门就先到这里。