简述 BIO 、NIO 模型
- BIO : 同步阻塞I/O(Block IO)
服务器实现模式为每一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,此处可以通过线程池机制进行优化。
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;/*** 多人聊天室 - 服务端*/
public class BioServer {static List<Socket> clientList = new ArrayList<>();public static void main(String[] args) throws Exception {int port = 8080;ServerSocket serverSocket = new ServerSocket(port);while (true) {Socket client = serverSocket.accept();System.out.println("客户端: " + client.getPort() + " 连接成功!");clientList.add(client);forwardProcess(client);}}/*** 转发处理*/public static void forwardProcess(Socket socket) {new Thread(new Runnable() {@Overridepublic void run() {while (true) {forwardMsg(socket);}}}).start();}/*** 转发消息*/public static void forwardMsg(Socket socket) {try {String msg = readMsg(socket);System.out.println(msg);for (Socket client : clientList) {if (client != socket) {writeMsg(client, msg);}}} catch (IOException e) {throw new RuntimeException(e);}}/*** 写入消息*/public static void writeMsg(Socket socket, String msg) throws IOException {PrintStream ps = new PrintStream(socket.getOutputStream());ps.println(msg);ps.flush();}/*** 读取消息*/public static String readMsg(Socket socket) throws IOException {InputStream inputStream = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));String msg;if ((msg = br.readLine()) != null) {msg = socket.getPort() + " 说: " + msg;}return msg;}}
import java.io.*;
import java.net.Socket;
import java.util.Scanner;public class BilClient {public static void main(String[] args) throws IOException {String ip = "127.0.0.1";int port = 8080;Socket client = new Socket(ip, port);readProcess(client);OutputStream os = client.getOutputStream();PrintStream ps = new PrintStream(os);Scanner scanner = new Scanner(System.in);while (true) {String input = scanner.nextLine();ps.println(input);ps.flush();}}/*** 读取处理*/public static void readProcess(Socket socket) {new Thread(() -> {while (true) {try {System.out.println(readMsg(socket));} catch (IOException e) {throw new RuntimeException(e);}}}).start();}/*** 读取消息*/public static String readMsg(Socket socket) throws IOException {InputStream inputStream = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));String msg;if ((msg = br.readLine()) != null) {}return msg;}
}
- NIO: 同步非阻塞式IO,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求会被注册到多路复用器上,多路复用器轮询到有 I/O 请求就会进行处理。
- Channel,翻译过来就是“通道”,就是数据传输的管道,类似于“流”,但是与“流”又有着区别。
- 既可以从Channel中读取数据,又可以写数据到Channel,但流的读写通常是单向的——输入输出流
- 通道可以异步读写
- 通道中的数据总是先读取到buffer(缓冲区),或者总是需要从一个buffer写入,不能直接访问数据
- 非阻塞特性:Channel在设计上采用了非阻塞的特性,它不会像传统的流一样在读写操作上阻塞线程,而是立即返回结果,告诉调用者当前的状态。这使得程序可以在等待数据准备的过程中同时进行其他操作,实现了非阻塞IO。
- 事件通知机制:Channel通常搭配选择器(Selector)来使用,选择器能够检测多个Channel的就绪状态,如是否可读、可写等,并通过事件通知(例如轮询或回调)及时地通知程序哪些Channel处于就绪状态,从而可以进行相应的读写操作。这种机制支持程序实现异步IO模型。
- 操作系统底层支持:Channel的异步读写也依赖于操作系统底层的异步IO支持。Java NIO中的Channel实际上是对操作系统底层异步IO的封装和抽象,利用了操作系统提供的异步IO机制来实现其自身的异步读写功能。
- Buffer是一个对象,里面是要写入或者读出的数据,在java.nio库中,所有的数据都是用缓冲区处理的。
- 在读取数据时,它是直接读到缓冲区中的;在写入数据时,也是直接写到缓冲区中,任何时候访问Channel中的数据,都是通过缓冲区进行操作的。
- 缓冲区实质上是一个数组,通常是一个字节数组ByteBuffer,当然也有其他类型的:
- Selector被称为选择器,Selector会不断地轮询注册在其上的Channel,如果某个Channel上发生读或写事件,这个Channel就被判定处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取到就绪Channel的集合,进行后续的I/O操作。
- 一个多路复用器Selector可以同时轮询多个Channel,JDK使用了epoll()代替了传统的select实现,所以并没有最大连接句柄的限制,这意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Set;/*** NIO 聊天室 服务端*/
public class NioServer {private Integer port;ByteBuffer readBuffer = ByteBuffer.allocate(1024);ByteBuffer writerBuffer = ByteBuffer.allocate(1024);private Charset charset = Charset.forName("UTF-8");public NioServer(Integer port) {this.port = port;}public static void main(String[] args) {NioServer nioServer = new NioServer(8080);nioServer.start();}private void start() {try {// 开启socketServerSocketChannel server = ServerSocketChannel.open();// 设置非阻塞server.configureBlocking(false);// 绑定端口server.socket().bind(new InetSocketAddress(port));// 开启通道, 得到 Selector (选择器)Selector selector = Selector.open();// 注册 selector 监听事件server.register(selector, SelectionKey.OP_ACCEPT);System.out.println("启动服务器, 监听端口:" + port + "...");while (true) {// 阻塞监控所有注册的通道,当有对应的事件操作时, 会将SelectionKey放入 集合内部并返回事件数量selector.select();// 返回存有SelectionKey的集合Set<SelectionKey> selectionKeys = selector.selectedKeys();for (SelectionKey selectionKey : selectionKeys) {handle(selectionKey, selector);}// 处理后清理 selectionKeysselectionKeys.clear();}} catch (Exception e) {e.printStackTrace();}}/*** 事件处理*/private void handle(SelectionKey key, Selector selector) throws IOException {// SelectionKey 常用方法//isAcceptable() 是否是连接继续事件//isConnectable() 是否是连接就绪事件//isReadable() 是否是读就绪事件//isWritable() 是否是写就绪事件// SelectionKey 常用事件//SelectionKey.OP_ACCEPT 接收连接继续事件,表示服务器监听到了客户连接,服务器可以接收这个连接了//SelectionKey.OP_CONNECT 连接就绪事件,表示客户端与服务器的连接已经建立成功//SelectionKey.OP_READ 读就绪事件,表示通道中已经有了可读的数据,可以执行读操作了(通道目前有数据,可以进行读操作了)//SelectionKey.OP_WRITE 写就绪事件,表示已经可以向通道写数据了(通道目前可以用于写操作)//处理连接if (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel client = server.accept();client.configureBlocking(false);client.register(selector, SelectionKey.OP_READ);System.out.println(client.socket().getPort() + " 已建立连接 ...");}// 读取消息else if (key.isReadable()) {SocketChannel client = (SocketChannel) key.channel();String msg = client.socket().getPort() + " 说: " + readMsg(client);System.out.println(msg);forwardMsg(msg, client, selector);}}/*** 读取通道消息*/private String readMsg(SocketChannel client) throws IOException {readBuffer.clear();while (client.read(readBuffer) > 0) ;readBuffer.flip();return String.valueOf(charset.decode(readBuffer));}/*** 转发*/private void forwardMsg(String msg, SocketChannel client, Selector selector) throws IOException {for (SelectionKey key : selector.keys()) {Channel connectedClient = key.channel();if (connectedClient instanceof ServerSocketChannel) {continue;}if (key.isValid() && !client.equals(connectedClient)) {writerBuffer.clear();writerBuffer.put(charset.encode(msg));writerBuffer.flip();while (writerBuffer.hasRemaining())((SocketChannel) connectedClient).write(writerBuffer);}}}
}
import java.io.IOException;
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.nio.charset.Charset;
import java.util.Scanner;
import java.util.Set;/*** NIO 聊天室 客户端*/
public class NioClient {private String ip;private Integer port;private ByteBuffer writerBuffer = ByteBuffer.allocate(1024);private ByteBuffer readBuffer = ByteBuffer.allocate(1024);private Charset charset = Charset.forName("UTF-8");public NioClient(String ip, Integer port) {this.ip = ip;this.port = port;}public static void main(String[] args) {NioClient nioClient = new NioClient("127.0.0.1", 8080);nioClient.start();}public void start() {try {// 开启通道SocketChannel client = SocketChannel.open();// 设置非阻塞client.configureBlocking(false);Selector selector = Selector.open();client.register(selector, SelectionKey.OP_CONNECT);client.connect(new InetSocketAddress(ip, port));while (true) {selector.select();Set<SelectionKey> selectionKeys = selector.selectedKeys();for (SelectionKey selectionKey : selectionKeys) {handle(selectionKey, selector);}selectionKeys.clear();}} catch (Exception e) {e.printStackTrace();}}private void handle(SelectionKey key, Selector selector) throws IOException {// 处理连接事件if (key.isConnectable()) {SocketChannel client = (SocketChannel) key.channel();if (client.isConnectionPending()) {client.finishConnect();// 处理用户输入new Thread(() -> {Scanner scanner = new Scanner(System.in);while (true) {String msg = scanner.nextLine();writerBuffer.clear();writerBuffer.put(charset.encode(msg));writerBuffer.flip();while (writerBuffer.hasRemaining()) {try {client.write(writerBuffer);} catch (IOException e) {throw new RuntimeException(e);}}}}).start();}client.register(selector, SelectionKey.OP_READ);}// 读取消息信息else if (key.isReadable()) {SocketChannel client = (SocketChannel) key.channel();String s = readMsg(client);System.out.println(s);}}private String readMsg(SocketChannel client) throws IOException {readBuffer.clear();while (client.read(readBuffer) > 0) ;readBuffer.flip();return String.valueOf(charset.decode(readBuffer));}
}
相关文章:

简述 BIO 、NIO 模型
BIO : 同步阻塞I/O(Block IO) 服务器实现模式为每一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,此处可以通过线程池机制进行优化。 impo…...
【Python小练】随机验证码
题目 提示输出含数字、字母的四位随机数,输入提示的验证码进行验证,验证码正确结束程序,验证码错误继续输入。 分析 我们可以通过random模块生成0到9的随机数,也可以通过生成65到90的随机数,将65到90的随机ASCLL码转换…...
《1w实盘and大盘基金预测 day30》
今日预测: 3123-3150-3177 探底回升,震荡上涨,收小红小绿 双创指数后期上涨的幅度也是会大于上证的,四月底的时候就提醒建仓。 关注板块:医疗、地产、电力、证券 这周预测 这周上证指数最高看到3200 继续看涨&#…...

软件工程案例学习-图书管理系统-面向对象方法
文档编号:LMS_1 版 本 号:V1.0 ** ** ** ** ** ** 文档名称:需求分析规格说明书 项目名称:图书管理系统 项目负责人:计敏 胡杰 ** ** …...
python:机器学习特征优选(过滤法)
作者:CSDN @ _养乐多_ 本文将介绍如何使用 python 语言使用过滤法进行机器学习特征优选。分别有F值、互信息(Mutual Information,MI)、方差阈值(Variance Threshold,VT)、相关性方法。 文章目录 一、特征数据1.1 将用于分析的数据从GEE下载到本地1.2 从其他方法获取二、…...

CH32V 系列 MCU IAP 使用函数形式通过传参形式灵活指定APP跳转地址
参考: CH32V 系列 MCU IAP 升级跳转方法 CH32V103 的 IAP 问题(跳转及中断向量表重定位) 1. 沁恒的RISC-V内核MCU的IAP跳转示例程序简要分析 沁恒的RISC-V内核的MCU比如CH32V203、CH32V307等系列的EVT包中IAP升级的示例程序中都是通过使能软件中断之后&…...

教程分享:如何为跨境电商、外贸、国际展会制作二维码?
不论是做跨境电商、在全球做产品推广,还是国外的餐厅运营、参加国际展会,或者是做创意户外广告、制作个性化的个人名片、有趣的产品包装……只要是在国外使用二维码,你都可以在QR Tiger去制作您需要的二维码! 一、认识QR Tiger 二…...

ComfyUI 基础教程(十三):ComfyUI-Impact-Pack 面部修复
SD的WebUI 中的面部修复神器 ADetailer,无法在ComfyUI 中使用。那么如何在ComfyUI中进行面部处理呢?ComfyUI 中也有几个面部修复功能,比如ComfyUI Impact Pack(FaceDetailer),以及换脸插件Reactor和IPAdapter。 ComfyUI-Impact-Pack 是一个功能强大的插件,专为 ComfyUI …...

专题五_位运算(2)
目录 面试题 01.01. 判定字符是否唯一 解析 题解 268. 丢失的数字 解析 题解 371. 两整数之和 解析 题解 面试题 01.01. 判定字符是否唯一 面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode) 解析 题解 class Solution { public:bool isUnique…...
ZCC5503 18V 1A 6uA低静态功耗 同步降压控制器
1. 概要 ZCC5503R 是一款基准电压源、振荡电路、 比较器 PWM/PFM 控制器构成的 CMOS 降压电路调整器,利用 PWM/PFM 自动切换控制电路达到可调占空比,具有全输入电压范围(3~18V )内的低纹波、高效率及大电流输出等特点. 2. 产…...
python操作minio中常见错误
因为我参考minio的文档操作,当时文档并不是很详细,这篇博文会统一记录自己所遇到的问题。以下的每个标题都是具体的错误信息。 minio-py文档 错误1:SSL: WRONG_VERSION_NUMBER 这个错误的原因是在创建minio的客户端时候没有关闭SSL,请使用如…...

SpringCloud-Seata分布式事务的环境搭建搭建
目录 一、版本说明 二、建立Seata Server数据库(TC-带头大哥的数据库) 三、业务库建表 四、安装Seata-Server 4.1 虚拟机里新建一个/opt/seate/seata-server文件夹,在seate文件夹下新建一个docker-compose.yml 文件 4.2 运行容器 4.3 在na…...

ChatGPT4 Turbo 如何升级体验?官网如何使用最新版GPT-4 Turbo?
本文会教大家如何教大家升级自己的GPT4到GPT4 Turbo,同时检验自己的GPT4 Turbo是否是最新版本的GPT-4-Turbo-2024-04-09 说明 新版GPT-4 Turbo再次重夺大模型排行榜王座,超越了Claude 3 Opus。 最新版本的GPT-4 Turbo被命名为GPT-4-Turbo-2024-04-09。…...

如何利用工作流自定义一个AI智能体
选择平台 目前已经有不少大模型平台都提供自定义智能体的功能,比如 百度的文心 https://agents.baidu.com/ 阿里的百炼平台 https://bailian.console.aliyun.com/。 今天再来介绍一个平台扣子(https://www.coze.cn/),扣子是…...
嵌入式学习day12
每日面试题 static 关键字在 全局变量、局部变量、函数的区别? ①全局变量static:改变作用域,改变(限制)其使用范围。 只初始化一次,防止在其他文件单元中被引用。全局变量的作用域是整个源程序ÿ…...

【Leetcode 42】 接雨水-单调栈解法
基础思路: 维持栈单调递减,一旦出现元素大于栈顶元素,就可以计算雨水量,同时填坑(弹出栈顶元素) 需要注意: 单调栈通常保存的是下标,用于计算距离 public static int trap2(int[…...

Python 贪吃蛇
文章目录 效果图:项目目录结构main.pygame/apple.pygame/base.pygame/snake.pyconstant.py 效果图: 项目目录结构 main.py from snake.game.apple import Apple # 导入苹果类 from snake.game.base import * # 导入游戏基类 from snake.game.snake im…...
计算机网络 2.4差错检验与校正
第四节 差错检验与校正 一、认识检验与校正 1.差错形成原因 内部因素(随机错):噪声脉冲、脉动噪声、衰减、延迟失真等。 外部因素(突发错):电磁干扰、太阳噪声、工业噪声等。 2.差错控制编码分类&#…...
uniapp遍历数组对象的常见方法
在 UniApp 中,遍历数组对象的方法与在普通 JavaScript 中是相同的。UniApp 是一个使用 Vue.js 开发所有前端应用的框架,因此你可以使用 Vue.js 和 JavaScript 的语法来遍历数组对象。 以下是一些常见的遍历数组对象的方法: 使用 for 循环 …...

Milvus向量数据库(一)Milvus存储byte[]类型源向量数据
两种路线: 第一种是把byte[]转换为List< float >,然后存储到Milvus的floatVector中第二种是把byte[]转换为ByteBuffer,然后存储到Milvus的BinaryVector中 步骤: 我先用的是第一种,但是在转换float过程中&…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...