首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈Java BIO 和 NIO

浅谈Java BIO 和 NIO

作者头像
搬砖俱乐部
发布2020-03-20 11:26:00
4440
发布2020-03-20 11:26:00
举报
文章被收录于专栏:BanzClubBanzClub

01

分析 BIO 过程

Socket 是什么呢,其实就是一个 有 IP地址+端口,并且持有文件描述符的对象,操作系统通过 ip + port 建立 TCP 连接;通过文件描述符来读取传输的数据。无论操作系统内核,还是JDK,都会定义一个Socket的数据结构。

而通常网络连接,是一个客户端\服务端模型,在 Java世界里,ServerSocket 用来实现服务端接收网络请求,Socket 在客户端用于实现网络连接建立,并发送\接收传输数据,在服务端用于承接每一个连接请求。抽象一点理解,就像两个Socket在实际通信。

ServerSocket 和 Socket 具体行为的实现为 SocketImpl,SocketImpl 封装了服务端bind、listen等内核操作函数,用来建立Socket监听对象;也封装了客户端connect操作函数,用来与服务端创建连接,当然还包括输入、输出的数据流对象;

Server 端

  • 创建 网络监听连接
  • 监听网络地址 0.0.0.0
  • 绑定端口
  • 设置 监听队列长度 backlog
  • 操作系统底层支持

当 socket 调用 accept 时候,实际上会调用操作系统的 accept函数,而 当前 ServerSocket 会阻塞等待,直到操作系统 将 一个客户端连接 封装成 Socket 返回给 ServerSocket;服务端拿到一个Socket对象才跳出阻塞,去执行具体的Socket响应逻辑(即,Handler),通常我们会使用一个线程池去执行它。

注:其实 对于每一个 网络进程 来说,操作系统会用很多 文件描述符(对应的内核地址),用来操作系统读取数据,通常进程的文件描述符 是有上限的(大概 是 1024)。

public class BIOServer {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(9999);
            System.out.println("服务端已经启动!");
            ExecutorService executor = Executors.newFixedThreadPool(10);
            boolean flag = true;
            while (flag) {
                Socket accept = serverSocket.accept();
                executor.execute(new BIOHandler(accept));
            }
            executor.shutdown();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class BIOHandler implements Runnable {
    Socket socket;
    BIOHandler(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = bufferedInputStream.read(buffer)) != -1) {
                String s = new String(buffer, 0, len);
                System.out.println(s);
            }
            inputStream.close();
            bufferedInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Client 端

根据 ip + port,去与目标地址创建连接,经过三次握手,成功创建连接,就可以通过输入、输出流与服务端进行数据交互;

public class BIOClient {
    private static final BufferedReader KEYBOARD_INPUT = new BufferedReader(new InputStreamReader(System.in));
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost", 9999);
            boolean flag = true;
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
            while (flag) {
                String str = KEYBOARD_INPUT.readLine();
                bufferedOutputStream.write(str.getBytes());
                bufferedOutputStream.flush();
            }
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

02

分析 NIO 过程

Channel 持有 Socket、fd、SelectionKeys[] ,ServerSocketChannel 和 SocketChannel,不能直接读取数据,需要使用 Buffer 缓冲区技术来获取数据,当然缓冲区可以有很多种,ByteBuffer、或者内存映射(MappedByteBuffer),就是用户线程与内核共用操作系统的内核缓冲区,这样程序可以不拷贝内核数据到用户空间,而直接操作数据,提升效率;

套接字通道设置为阻塞,当调用 channel 的 accept时候,会直接返回结果,而不是阻塞等待对应事件的到达;

channel需要将 selector 注册,并且绑定接收就绪事件;

所有的Channel 需要由 Selector 管理,当由连接就行,可读、可写等IO事件时候,可以通过Selector.select方法检测到,然后通过遍历Selector上的所有就绪事件,进行相应事件的IO操作;

对于 Selector.select 方法,不同操作系统的实现不一样,select、poll和epoll等;


https://blog.csdn.net/u011990285/article/details/88125071?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

https://segmentfault.com/a/1190000003063859/

https://blog.csdn.net/daaikuaichuan/article/details/83475809

sendfile“零拷贝”和mmap内存映射

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 BanzClub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档