Java NIO学习笔记--02

SocketChannel

  • Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道。可以通过以下2种方式创建SocketChannel:
    • 1.打开一个SocketChannel并连接到互联网上的某台服务器。
    • 2.一个新连接到达ServerSocketChannel时,会创建一个SocketChannel。
  • 1.打开 SocketChannel
    • 下面是SocketChannel的打开方式:
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
  • 2.关闭 SocketChannel
    • 当用完SocketChannel之后调用SocketChannel.close()关闭SocketChannel:
socketChannel.close();
  • 3.从 SocketChannel 读取数据
    • 要从SocketChannel中读取数据,调用一个read()的方法之一。
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);
  • 首先,分配一个Buffer。从SocketChannel读取到的数据将会放到这个Buffer中。

  • 然后,调用SocketChannel.read()。该方法将数据从SocketChannel 读到Buffer中。read()方法返回的int值表示读了多少字节进Buffer里。如果返回的是-1,表示已经读到了流的末尾(连接关闭了)。

  • 4.写入 SocketChannel
    • 写数据到SocketChannel用的是SocketChannel.write()方法,该方法以一个Buffer作为参数。
String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
    channel.write(buf);
}
  • 注意SocketChannel.write()方法的调用是在一个while循环中的。Write()方法无法保证能写多少字节到SocketChannel。所以,我们重复调用write()直到Buffer没有要写的字节为止。
  • 5.非阻塞模式
    • 可以设置 SocketChannel 为非阻塞模式(non-blocking mode).设置之后,就可以在异步模式下调用connect(), read() 和write()了。
  • 6.connect()
    • 如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()的方法。像这样:
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

while(! socketChannel.finishConnect() ){
    //wait, or do something else...
}
  • 7.write()
    • 非阻塞模式下,write()方法在尚未写出任何内容时可能就返回了。所以需要在循环中调用write()。
  • 8.read()
    • 非阻塞模式下,read()方法在尚未读取到任何数据时可能就返回了。所以需要关注它的int返回值,它会告诉你读取了多少字节。
  • 9.非阻塞模式与选择器
    • 非阻塞模式与选择器搭配会工作的更好,通过将一或多个SocketChannel注册到Selector,可以询问选择器哪个通道已经准备好了读取,写入等。Selector与SocketChannel的搭配使用会在后面详讲。

ServerSocketChannel

  • Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 就像标准IO中的ServerSocket一样。ServerSocketChannel类在 java.nio.channels包中。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();

    //do something with socketChannel...
}
  • 1.打开 ServerSocketChannel
    • 通过调用 ServerSocketChannel.open() 方法来打开ServerSocketChannel.如:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
  • 2.关闭 ServerSocketChannel
    • 通过调用ServerSocketChannel.close() 方法来关闭ServerSocketChannel.
serverSocketChannel.close();
  • 3.监听新进来的连接
    • 通过 ServerSocketChannel.accept() 方法监听新进来的连接。当 accept()方法返回的时候,它返回一个包含新进来的连接的 SocketChannel。因此, accept()方法会一直阻塞到有新连接到达。
      • 通常不会仅仅只监听一个连接,在while循环中调用 accept()方法.
while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();

    //do something with socketChannel...
}
  • 4.非阻塞模式
    • ServerSocketChannel可以设置成非阻塞模式。在非阻塞模式下,accept() 方法会立刻返回,如果还没有新进来的连接,返回的将是null。 因此,需要检查返回的SocketChannel是否是null.
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();

    if(socketChannel != null){
        //do something with socketChannel...
    }
}

DatagramChannel

  • DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。
  • 1.打开 DatagramChannel
    • 下面是 DatagramChannel 的打开方式:
DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9999));
  • 2.接收数据
    • 通过receive()方法从DatagramChannel接收数据,如:
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);
  • receive()方法会将接收到的数据包内容复制到指定的Buffer. 如果Buffer容不下收到的数据,多出的数据将被丢弃。
  • 3.发送数据
    • 通过send()方法从DatagramChannel发送数据,如:
String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();

int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com", 80));
  • 这个例子发送一串字符到”jenkov.com”服务器的UDP端口80。 因为服务端并没有监控这个端口,所以什么也不会发生。也不会通知你发出的数据包是否已收到,因为UDP在数据传送方面没有任何保证。
  • 4.连接到特定的地址
    • 可以将DatagramChannel“连接”到网络中的特定地址的。由于UDP是无连接的,连接到特定地址并不会像TCP通道那样创建一个真正的连接。而是锁住DatagramChannel ,让其只能从特定地址收发数据。
channel.connect(new InetSocketAddress("jenkov.com", 80));
  • 当连接后,也可以使用read()和write()方法,就像在用传统的通道一样。只是在数据传送方面没有任何保证。
int bytesRead = channel.read(buf);
int bytesWritten = channel.write(but);

Pipe

  • Java NIO Pipe管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。
    • Pipe原理的图示:
      这里写图片描述
    • 1.创建管道
      • 通过Pipe.open()方法打开管道。
Pipe pipe = Pipe.open();
  • 2.向管道写数据
    • 要向管道写数据,需要访问sink通道。
Pipe.SinkChannel sinkChannel = pipe.sink();
  • 通过调用SinkChannel的write()方法,将数据写入SinkChannel
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
    sinkChannel.write(buf);
}
  • 3.从管道读取数据
    • 从读取管道的数据,需要访问source通道,
Pipe.SourceChannel sourceChannel = pipe.source();
  • 调用source通道的read()方法来读取数据
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = sourceChannel.read(buf);

Java NIO与IO

  • 1.Java NIO和IO的主要区别
IO NIO
面向流 面向缓冲
阻塞IO 非阻塞IO
选择器
  • 2.面向流与面向缓冲
    • Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。
    • 此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。
    • Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。
    • 但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
  • 3.阻塞与非阻塞IO
    • Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。
    • Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。
    • 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
    • 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
  • 4.选择器(Selectors)
    • Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
  • 5.NIO和IO如何影响应用程序的设计
    • NIO和IO如何影响应用程序的设计
      • 对NIO或IO类的API调用。
      • 数据处理。
      • 用来处理数据的线程数。
  • 5.1.API调用
    • 当然,使用NIO的API调用时看起来与使用IO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。
  • 5.2.数据处理

    • 使用纯粹的NIO设计相较IO设计,数据处理也受到影响。

    • 在IO设计中,我们从InputStream或 Reader逐字节读取数据。假设你正在处理一基于行的文本数据流

BufferedReader reader = new BufferedReader(new InputStreamReader(input));

String nameLine   = reader.readLine();
String ageLine    = reader.readLine();
String emailLine  = reader.readLine();
String phoneLine  = reader.readLine();

………..


转载自并发编程网 – ifeve.com本文链接地址: Java NIO系列教程(十二) Java NIO与IO

猜你喜欢

转载自blog.csdn.net/m0_37204491/article/details/79195330