我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java IO网络模型如何实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java IO网络模型如何实现

本篇内容主要讲解“Java IO网络模型如何实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java IO网络模型如何实现”吧!

    在开始本篇文章内容之前,有一个简单的关于Socket的知识需要说明:在进行网络通信的时候,需要一对Socket,一个运行于客户端,一个运行于服务端,同时服务端还会有一个服务端Socket,用于监听客户端的连接。下图进行一个简单示意。

    Java IO网络模型如何实现

    那么整个通信流程如下所示。

    • 服务端运行后,会在服务端创建listen-socketlisten-socket会绑定服务端的ipport,然后服务端进入监听状态;

    • 客户端请求服务端时,客户端创建connect-socketconnect-socket描述了其要连接的服务端的listen-socket,然后connect-socketlisten-socket发起连接请求;

    • connect-socketlisten-socket成功连接后(TCP三次握手成功),服务端会为已连接的客户端创建一个代表该客户端的client-socket,用于后续和客户端进行通信;

    • 客户端与服务端通过socket进行网络IO操作,此时就实现了客户端和服务端中的不同进程的通信。

    需要知道的就是,在客户端与服务端通信的过程中,出现了三种socket,分别是。

    • listen-socket。是服务端用于监听客户端建立连接的socket

    • connect-socket。是客户端用于连接服务端的socket

    • client-socket。是服务端监听到客户端连接请求后,在服务端生成的与客户端连接的socket

    (注:上述中的socket,可以被称为套接字,也可以被称为文件描述符。)

    正文

    一. BIO

    BIO,即同步阻塞IO模型。用户进程调用read时发起IO操作,此时用户进程由用户态转换到内核态,只有在内核态中将IO操作执行完后,才会从内核态切换回用户态,这期间用户进程会一直阻塞。

    BIO示意图如下。

    Java IO网络模型如何实现

    简单的BIOJava编程实现如下。

    服务端实现

    public class BioServer {    public static void main(String[] args) throws IOException {        // 创建listen-socket        ServerSocket listenSocket = new ServerSocket(8080);        // 进入监听状态,是一个阻塞状态        // 有客户端连接时从监听状态返回        // 并创建代表这个客户端的client-socket        Socket clientSocket = listenSocket.accept();        // 获取client-socket输入流        BufferedReader bufferedReader = new BufferedReader(                new InputStreamReader(clientSocket.getInputStream()));        // 读取客户端发送的数据        // 如果数据没准备好,会进入阻塞状态        String data = bufferedReader.readLine();        System.out.println(data);        // 获取client-socket输出流        BufferedWriter bufferedWriter = new BufferedWriter(                new OutputStreamWriter(clientSocket.getOutputStream()));        // 服务端向客户端发送数据        bufferedWriter.write("来自服务端的返回数据\n");        // 刷新流        bufferedWriter.flush();    }}

    客户端实现

    public class BioClient {    public static final String SERVER_IP = "127.0.0.1";    public static final int SERVER_PORT = 8080;    public static void main(String[] args) throws IOException {        // 客户端创建connect-socket        Socket connectSocket = new Socket(SERVER_IP, SERVER_PORT);        // 获取connect-socket输出流        BufferedWriter bufferedWriter = new BufferedWriter(                new OutputStreamWriter(connectSocket.getOutputStream()));        // 客户端向服务端发送数据        bufferedWriter.write("来自客户端的请求数据\n");        // 刷新流        bufferedWriter.flush();        // 获取connect-socket输入流        BufferedReader bufferedReader = new BufferedReader(                new InputStreamReader(connectSocket.getInputStream()));        // 读取服务端发送的数据        String returnData = bufferedReader.readLine();        System.out.println(returnData);    }}

    BIO的问题就在于服务端在accept时是阻塞的,并且在主线程中,一次只能accept一个SocketacceptSocket后,读取客户端数据时又是阻塞的。

    二. Non Blocking IO

    Non Blocking IO,即同步非阻塞IO。是用户进程调用read时,用户进程由用户态转换到内核态后,此时如果没有系统资源数据能够被读取到内核缓冲区中,返回read失败,并从内核态切换回用户态。也就是用户进程发起IO操作后会立即得到一个操作结果。

    Non Blocking IO示意图如下所示。

    Java IO网络模型如何实现

    简单的Non Blocking IOJava编程实现如下。

    public class NonbioServer {    public static final List<SocketChannel> clientSocketChannels = new ArrayList<>();    public static void main(String[] args) throws Exception {        // 客户端创建listen-socket管道        // 管道支持非阻塞模式和同时读写        ServerSocketChannel listenSocketChannel = ServerSocketChannel.open();        // 设置为非阻塞模式        listenSocketChannel.configureBlocking(false);        // 绑定监听的端口号        listenSocketChannel.socket().bind(new InetSocketAddress(8080));        // 在子线程中遍历clientSocketChannels并读取客户端数据        handleSocketChannels();        while (true) {            // 非阻塞方式监听客户端连接            // 如果无客户端连接则返回空            // 有客户端连接则创建代表这个客户端的client-socket管道            SocketChannel clientSocketChannel = listenSocketChannel.accept();            if (clientSocketChannel != null) {                // 设置为非阻塞模式                clientSocketChannel.configureBlocking(false);                // 添加到clientSocketChannels中                // 用于子线程遍历并读取客户端数据                clientSocketChannels.add(clientSocketChannel);            } else {                LockSupport.parkNanos(1000 * 1000 * 1000);            }        }    }    public static void handleSocketChannels() {        new Thread(() -> {            while (true) {                // 遍历每一个client-socket管道                Iterator<SocketChannel> iterator = clientSocketChannels.iterator();                while (iterator.hasNext()) {                    SocketChannel clientSocketChannel = iterator.next();                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);                    int read = 0;                    try {                        // 将客户端发送的数据读取到ByteBuffer中                        // 这一步的操作也是非阻塞的                        read = clientSocketChannel.read(byteBuffer);                    } catch (IOException e) {                        // 移除发生异常的client-socket管道                        iterator.remove();                        e.printStackTrace();                    }                    if (read == 0) {                        System.out.println("客户端数据未就绪");                    } else {                        System.out.println("客户端数据为:" + new String(byteBuffer.array()));                    }                }                LockSupport.parkNanos(1000 * 1000 * 1000);            }        }).start();    }}

    上述是Non Blocking IO的一个简单服务端的实现,相较于BIO,服务端在accept时是非阻塞的,在读取客户端数据时也是非阻塞的,但是还是存在如下问题。

    • 一次只能accept一个Socket

    • 需要在用户进程中遍历所有的SocketChannel并调用read() 方法获取客户端数据,此时如果客户端数据未准备就绪,那么这一次的read() 操作的开销就是浪费的。

    三. IO多路复用

    在上述的BIONon Blocking IO中,一次系统调用,只会获取一个IO的状态,而如果采取IO多路复用机制,则可以一次系统调用获取多个IO的状态。

    也就是获取多个IO的状态可以复用一次系统调用。

    最简单的IO多路复用方式是基于select模型实现,步骤如下。

    • 在用户进程中将需要监控的IO文件描述符(Socket)注册到IO多路复用器中;

    • 执行select操作,此时用户进程由用户态转换到内核态(一次系统调用),然后在内核态中会轮询注册到IO多路复用器中的IO是否准备就绪,并得到所有准备就绪的IO的文件描述符列表,最后返回这些文件描述符列表;

    • 用户进程在select操作返回前会一直阻塞,直至select操作返回,此时用户进程就获得了所有就绪的IO的文件描述符列表;

    • 用户进程获得了就绪的IO的文件描述符列表后,就可以对这些IO进行相应的操作了。

    换言之,IO多路复用中,只需要一次系统调用,IO多路复用器就可以告诉用户进程,哪些IO已经准备就绪可以进行操作了,而如果不采用IO多路复用,则需要用户进程自己遍历每个IO并调用accept() 或者read() 方法去判断,且一次accept() 或者read() 方法调用只能判断一个IO

    四. NIO

    NIO,即New IO。关于NIO,有如下三大组件。

    • channel(管道)。介于buffer(字节缓冲区)和Socket(套接字)之间,用于数据的读写操作;

    • buffer(字节缓冲区)。是用户程序和channel(管道)之间进行读写数据的中间区域;

    • selectorIO多路复用器)。服务端的listen-socketclient-socket,客户端的connect-socket,都可以注册在selector上,注册的时候还需要指定监听的事件,比如为listen-socket指定监听的事件为ACCEPT事件,该事件发生则表示客户端建立了连接,还比如为client-socket指定监听的事件为READ事件,该事件发生则表示客户端发送的数据已经可读。

    NIO的代码实现如下所示。

    服务端实现

    public class NioServer {    private static Selector selector;    public static void main(String[] args) {        try {            // 开启并得到多路复用器            selector = Selector.open();            // 服务端创建listen-socket管道            ServerSocketChannel listenSocketChannel = ServerSocketChannel.open();            // 设置为非阻塞模式            listenSocketChannel.configureBlocking(false);            // 为管道绑定端口            listenSocketChannel.socket().bind(new InetSocketAddress(8080));            // 将listen-socket管道注册到多路复用器上,并指定监听ACCEPT事件            listenSocketChannel.register(selector, SelectionKey.OP_ACCEPT);            while (true) {                // 获取发生的事件,这个操作是阻塞的                selector.select();                // 拿到有事件发生的SelectionKey集合                // SelectionKey表示管道与多路复用器的绑定关系                Set<SelectionKey> selectionKeys = selector.selectedKeys();                // 遍历每个发生的事件,然后判断事件类型                // 根据事件类型,进行不同的处理                Iterator<SelectionKey> iterator = selectionKeys.iterator();                while (iterator.hasNext()) {                    SelectionKey selectionKey = iterator.next();                    iterator.remove();                    if (selectionKey.isAcceptable()) {                        // 处理客户端连接事件                        handlerAccept(selectionKey);                    } else if (selectionKey.isReadable()) {                        // 处理客户端数据可读事件                        handlerRead(selectionKey);                    }                }                LockSupport.parkNanos(1000 * 1000 * 1000);            }        } catch (IOException e) {            e.printStackTrace();        }    }    private static void handlerAccept(SelectionKey selectionKey) {        // 从事件中获取到listen-socket管道        ServerSocketChannel listenSocketChannel = (ServerSocketChannel) selectionKey.channel();        try {            // 为连接的客户端创建client-socket管道            SocketChannel clientSocketChannel = listenSocketChannel.accept();            // 设置为非阻塞模式            clientSocketChannel.configureBlocking(false);            // 将client-socket管道注册到多路复用器上,并指定监听READ事件            clientSocketChannel.register(selector, SelectionKey.OP_READ);            // 给客户端发送数据            clientSocketChannel.write(ByteBuffer.wrap("连接已建立\n".getBytes()));        } catch (IOException e) {            e.printStackTrace();        }    }    private static void handlerRead(SelectionKey selectionKey) {        // 从事件中获取到client-socket管道        SocketChannel clientSocketChannel = (SocketChannel) selectionKey.channel();        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);        try {            // 读取客户端数据            int read = clientSocketChannel.read(byteBuffer);            if (read <= 0) {                // 关闭管道                clientSocketChannel.close();                // 从多路复用器移除绑定关系                selectionKey.cancel();            } else {                System.out.println(new String(byteBuffer.array()));            }        } catch (IOException e1) {            try {                // 关闭管道                clientSocketChannel.close();            } catch (IOException e2) {                e2.printStackTrace();            }            // 从多路复用器移除绑定关系            selectionKey.cancel();            e1.printStackTrace();        }    }}

    客户端实现

    public class NioClient {    private static Selector selector;    public static final String SERVER_IP = "127.0.0.1";    public static final int SERVER_PORT = 8080;    public static void main(String[] args) {        try {            // 开启并得到多路复用器            selector = Selector.open();            // 创建connect-socket管道            SocketChannel connectSocketChannel = SocketChannel.open();            // 设置为非阻塞模式            connectSocketChannel.configureBlocking(false);            // 设置服务端IP和端口            connectSocketChannel.connect(new InetSocketAddress(SERVER_IP, SERVER_PORT));            // 将connect-socket管道注册到多路复用器上,并指定监听CONNECT事件            connectSocketChannel.register(selector, SelectionKey.OP_CONNECT);            while (true) {                // 获取发生的事件,这个操作是阻塞的                selector.select();                // 拿到有事件发生的SelectionKey集合                // SelectionKey表示管道与多路复用器的绑定关系                Set<SelectionKey> selectionKeys = selector.selectedKeys();                // 遍历每个发生的事件,然后判断事件类型                // 根据事件类型,进行不同的处理                Iterator<SelectionKey> iterator = selectionKeys.iterator();                while (iterator.hasNext()) {                    SelectionKey selectionKey = iterator.next();                    iterator.remove();                    if (selectionKey.isConnectable()) {                        // 处理连接建立事件                        handlerConnect(selectionKey);                    } else if (selectionKey.isReadable()) {                        // 处理服务端数据可读事件                        handlerRead(selectionKey);                    }                }                LockSupport.parkNanos(1000 * 1000 * 1000);            }        } catch (IOException e) {            e.printStackTrace();        }    }    private static void handlerConnect(SelectionKey selectionKey) throws IOException {        // 拿到connect-socket管道        SocketChannel connectSocketChannel = (SocketChannel) selectionKey.channel();        if (connectSocketChannel.isConnectionPending()) {            connectSocketChannel.finishConnect();        }        // 设置为非阻塞模式        connectSocketChannel.configureBlocking(false);        // 将connect-socket管道注册到多路复用器上,并指定监听READ事件        connectSocketChannel.register(selector, SelectionKey.OP_READ);        // 向服务端发送数据        connectSocketChannel.write(ByteBuffer.wrap("客户端发送的数据\n".getBytes()));    }    private static void handlerRead(SelectionKey selectionKey) {        // 拿到connect-socket管道        SocketChannel connectSocketChannel = (SocketChannel) selectionKey.channel();        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);        try {            // 读取服务端数据            int read = connectSocketChannel.read(byteBuffer);            if (read <= 0) {                // 关闭管道                connectSocketChannel.close();                // 从多路复用器移除绑定关系                selectionKey.cancel();            } else {                System.out.println(new String(byteBuffer.array()));            }        } catch (IOException e1) {            try {                // 关闭管道                connectSocketChannel.close();            } catch (IOException e2) {                e2.printStackTrace();            }            // 从多路复用器移除绑定关系            selectionKey.cancel();            e1.printStackTrace();        }    }}

    到此,相信大家对“Java IO网络模型如何实现”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    Java IO网络模型如何实现

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    Java IO网络模型如何实现

    本篇内容主要讲解“Java IO网络模型如何实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java IO网络模型如何实现”吧!在开始本篇文章内容之前,有一个简单的关于Socket的知识需要说
    2023-07-05

    Java IO网络模型实现解析

    这篇文章主要为大家介绍了Java IO网络模型实现解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-19

    Java IO模型与Java网络编程模型的对比

    本篇内容主要讲解“Java IO模型与Java网络编程模型的对比”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java IO模型与Java网络编程模型的对比”吧!IO模型介绍作者:cooffee
    2023-06-02

    总结网络IO模型与select模型的Python实例讲解

    网络I/O模型 人多了,就会有问题。web刚出现的时候,光顾的人很少。近年来网络应用规模逐渐扩大,应用的架构也需要随之改变。C10k的问题,让工程师们需要思考服务的性能与应用的并发能力。 网络应用需要处理的无非就是两大类问题,网络I/O,数
    2022-06-04

    Java IO中Reactor网络模型的概念是什么

    小编给大家分享一下Java IO中Reactor网络模型的概念是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、什么是 Reactor 模型:The rea
    2023-06-28

    java如何实现原型模式

    这篇文章主要介绍了java如何实现原型模式,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。定义: 通过复制现有的对象实例来创建新的对象实例。实现:实现Clonea
    2023-05-30

    BIO、NIO、IO多路复用模型详细介绍&Java NIO 网络编程

    文章目录 前言基本概念BIO过程NIO过程IO多路复用过程Java NIO编程Java NIO 核心概念Java NIO 示例 总结 前言 上文介绍了网络编程的基础知识,并基于 Java 编写了 BIO 的网络编程。我们知道
    2023-08-16

    java如何实现平面山脉模型

    这篇文章将为大家详细讲解有关java如何实现平面山脉模型,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。核心方法:递归其实当我第一次看到这个题目时,心中想的不就是个普通的递归吗,直接取两个点,
    2023-06-26

    如何理解Kubernetes 网络模型进阶

    本篇文章为大家展示了如何理解Kubernetes 网络模型进阶,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Kubernetes 网络模型来龙去脉容器网络发端于 Docker 的网络。Docker
    2023-06-04

    Reactor模型如何实现

    这篇文章主要介绍了Reactor模型如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Reactor模型如何实现文章都会有所收获,下面我们一起来看看吧。一. Reactor设计模式Reactor翻译过来的意
    2023-07-05

    R语言中怎么实现神经网络模型

    在R语言中,可以使用neuralnet包来实现神经网络模型。下面是一个简单的示例代码:# 安装并加载 neuralnet 包install.packages("neuralnet")library(neuralnet)# 创建一个数据
    R语言中怎么实现神经网络模型
    2024-03-07

    Java如何实现实现IO版学生管理系统

    这篇文章将为大家详细讲解有关Java如何实现实现IO版学生管理系统,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。具体内容如下图解: cade: student.javapub
    2023-06-29

    利用Pytorch实现ResNet网络构建及模型训练

    这篇文章主要为大家介绍了利用Pytorch实现ResNet网络构建及模型训练详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-17

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录