开发者

Java NIO通信基础示例详解

开发者 https://www.devze.com 2022-12-27 10:31 出处:网络 作者: 田埂
目录Java NIO 通信基础介绍NIO 和 OIO 的对比使用 FileChannel 完成文件复制的实践案例使用 DatagramChannel 数据包通道发送数据的实践案例使用 NIO 实现 Discard 服务器的实践案例Java NIO 通信基础介绍
目录
  • Java NIO 通信基础介绍
    • NIO 和 OIO 的对比
    • 使用 FileChannel 完成文件复制的实践案例
    • 使用 DatagramChannel 数据包通道发送数据的实践案例
    • 使用 NIO 实现 Discard 服务器的实践案例

Java NIO 通信基础介绍

高性能的 Java 通信,绝对离不开 Java NIO 技术,现在主流的技术框架或中间件服务器,都使 用了 Java NIO 技术,譬如:Tomcat、Jetty、Netty。

Java NIO 由以下三个核心组件组成:

  • Channel(通道)
  • Buffer(缓冲区)
  • Selector(选择器)

NIO 和 OIO 的对比

在 Java 中,NIO 和 OIO 的区别,主要体现在三个方面:

  • OIO 是面向流(Stream Oriented)的,NIO 是面向缓冲区(Buffer Oriented)的。 何谓面向流,何谓面向缓冲区呢? OIO 是面向字节流或字符流的,在一般的 OIO 操作中,我们以流式的方式顺序地从一个流中读取一个或多个字节,因此,我们不能随意地改变读取指针的位置。而在 NIO 操作中则不同,NIO 中引入了 Channel(通道)和 Buffer(缓冲区)的概念。读取和写入,只需要从通道中读取数据到缓冲区中,或将数据从缓冲区中写入到通道中。NIO 不像 OIO 那样是顺序操作,可以随意地读取 Buffer 中任意位置的数据。
  • OIO 的操作是阻塞的,而 NIO 的操作是非阻塞的。 NIO 如何做到非阻塞的呢?大家都知道,OIO 操作都是阻塞的,例如,我们调用一个 read 方法读取一个文件的内容,那么调用 re开发者_开发学习ad 的线程会被阻塞住,直到 read 操作完成。 而在 NIO 的非阻塞模式中,当我们调用 read 方法时,如果此时有数据,则 read 读取数据并返回;如果此时没有数据,则 read 直接返回,而不会阻塞当前线程。NIO 的非阻塞,是如何做到的呢?NIO 使用了通道和通道的多路复用技术。
  • OIO 没有选择器(Selector)概念,而 NIO 有选择器的概念。 NIO 的实现,是基于底层的选择器的系统调用。NIO 的选择器,需要底层操作系统提供支持。 而 OIO 不需要用到选择器。

使用 FileChannel 完成文件复制的实践案例

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class MyCopyFile {
    private File inFile;
    private File outFile;
    private FileInputStream fis = null;
    private FileOutputStream fos = null;
    private FileChannel fisChannel = null;
    private FileChannel fosChannel = null;
    //复制文件
    public void copyFile(String srcPath, String destPath) throws IOException {
        try {
            inFile = new File(srcPath);
            outFile = new File(destPath);
            fis = new FileInputStream(inFile);
            fos = new FileOutputStream(outFile);
            fisChannel = fis.getChannel();
            fosChannel = fos.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int length = -1;
            while ((length = fisChannel.read(buffer)) != -1) {
                buffer.flip();
                int outLenth = 0;
                while ((outLenth = fosChannel.write(buffer)) != 0) {
                    System.out.println("读取的字节数为:" + outLenth);
                }
                buffer.clear();
            }
            //强制刷新磁盘
            fosChannel.force(true);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            fosChannel.close();
            fos.close();
            fisChannel.close();
            fis.close();
        }
    }
    public static void main(String[] args) throws IOException {
        MyCopyFiletest = new MyCopyFile();
        String s1 = "D:\\maze.txt";
        String s2 = "D:\\maze1.txt";
        MyCopyFile.copyFile(s1, s2);
    }
}

使用 DatagramChannel 数据包通道发送数据的实践案例

功能:

获取用户的输入数据,通过 DatagramCOnlUbvzthannel 数据报通道,将数据发送到远程的服务器。

客户端代码:

public class Client {
    //Client发送信息
    public void send() throws IOException {
        DatagramChannel dChannel = DatagramChannel.open();
        dChannel.configureblocking(false);
        ByteBuffer buf = ByteBuffer.allocate(1024);
        Scanner sc = new Scanner(System.in);
    javascript    while (sc.hasNext()) {
            String s = sc.nextLine();
            buf.put(s.getBytes());
            buf.flip();
            dChannel.send(buf, new InetSocketAddress("127.0.0.1", 9999));
            buf.clear();
        }
        dChannel.close();
    }
    public static void main(String[] args) throws IOException {
        new Client().send();
    }
}

服务端代码:

public class Server {
    //服务端接收 用户发来的信息
    public void receive() throws IOException {
        DatagramChannel serverChannel = DatagramChannel.open();
        //设置成非阻塞模式
        serverChannel.configureBlocking(false);
        serverChannel.bind(new InetSocketAddress("127.0.0.1", 9999));
        Selector selector = Selector.open();
        serverChannel.register(selector, SelectionKey.OP_READ);
        while (selector.select() > 0) {
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (iterator.hasNext()) {
             javascript   SelectionKey next = iterator.next();
                if (next.isReadable()) {
                    SocketAddress receive = serverChannel.receive(buffer);
                    buffer.flip();
                    String s = new String(buffer.array(), 0, buffer.limit());
                    System.out.println(s);
                    buffer.clear();
                }
            }
            iterator.remove();
        }
        //关闭选择器和通道
        selector.close();
        serverChannel.close();
    }
    public static void main(String[] args) throws IOException {
        new Server().receive();
    }
}

使用 NIO 实现 Discard 服务器的实践案例

功能:

仅仅读取客户端通道的输入数据,读取完成后直接关闭客户端通道;并且读取到的数据直接抛弃掉

Discard 服务器代码:

public class SocketServerDemo {
    public void receive() throws IOException {
        //创建服务器的通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //设置为非阻塞模式
        serverSocketChannel.configureBlocking(false);
        //开启选择器
        Selector selector = Selector.open();
        //绑定链接
        serverSocketChannel.bind(new InetSocketAddress(9999));
        //将通道的某个IO事件  注册到选择器上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //轮询所有就绪的IO事件
        while (selector.select() > 0) {
            //逐个获取IO事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            //逐个判断该IO事件是否为想要的
            while (iterator.hasNext()) {
                SelectionKey next = iterator.next();
                if (next.isAcceptable()) {
                    //如果为该事件为“连接就绪”事件,就获取客户端的链接
                    SocketChannel clientSocket = serverSocketChannel.accept();
                    //将客户端的链接设置为非阻塞模式
                    clientSocket.configureBlocking(false);
                    //将新的通道的可读事件,注册到选择器上
                    clientSocket.register(selector, SelectionKey.OP_READ);
                } else if (next.isReadable()) {
                    //若IO事件为“可读事件”,读取数据
                    SocketChannel clientSocket = (SocketChannel) next.channel();
                    //创建缓冲区
                    ByteBuf编程fer buffer = ByteBuffer.allocate(1024);
                    int length = 0;
                    //读取事件 让后丢弃
                    while ((length = clientSocket.read(buffer)) > 0) {
                        buffer.flip();
                        String s = new String(buffer.array(), 0, length);
                        System.out.println(s);
                        buffer.clear();
                    }
                    clientSocket.close();
                }
                //移除选择键
                iterator.remove();
            }
        }
        serverSocketChannel.close();
    }
    public static void main(String[] args) throws IOException {
        new SocketServerDemo().receive();
    }
}

客户端的 DiscardClient 代码:

public class SocketClientDemo {
    public void socketClient() throws IOException {
        SocketChannel clientSocket = SocketChannel.open(new InetSocketAddress(9999));
        //切换成非阻塞模式
        clientSocket.configureBlocking(false);
        //如果没有连接完成 就一直链接
        while (!clientSocket.finishConnect()){
        }
        //执行到这里说明已经连接完成了
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("Hello SocketService".getBytes());
        buffer.flip();
        clientSocket.write(buffer);
        clientSocket.shutdownInput();
        clientSocket.close();
    }
    public static void main(String[] args) throws IOException {
        new SocketClientDemo().socketClient();
    }
}

与 Java OIO 相比,Java NIO 编程大致的特点如下:

(1)在 NIO 中,服务器接收新连接的工作,是异步进行的。不像 Java 的 OIO 那样,服务器监听连接,是同步的、阻塞的。NIO 可以通过选择器(也可以说成:多路复用器),后续不断地轮询选择器的选择键集合,选择新到来的连接。

(2)在 NIO 中,SocketChannel 传输通道的读写操作都是异步的。如果没有可读写的数据,负责 IO 通信的线程不会同步等待。这样,线程就可以处理其他连接的通道;不需要像 OIO 那样,线程一直阻塞,等待所负责的连接可用为止。

(3)在 NIO 中,一个选择器线程可以同时处理成千上万个客编程户端连接,性能不会随着客户端的增加而线性下降。

以上就是Java NIO通信基础示例详解的详细内容,更多关于Java NIO通信基础的资料请关注我们其它相关文章!

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号