当前位置: 首页 > news >正文

Netty入门基础:IO模型中BIO\NIO概念及区别【附演示代码】

文章目录

  • 😀BIO
    • 💢实战demo
  • 🌈NIO
    • 🏍Buffer
      • 核心属性
      • 核心方法
    • 🎗Channel
    • 🎈Selector
      • 核心方法
    • 🧨实战demo
  • 🎨粘包与半包

😀BIO

传统IO模型,同步阻塞,每个来自客户端的连接,服务端就专门启动一个线程进行处理,如果这个连接不做任何事情,会造成不必要的线程开销。

适用于连接数目小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,程序简单易理解。

💢实战demo

验证在BIO模型下,服务端中一个线程只能处理一个客户端的连接。

服务端代码,使用SocketChannel,监听9090端口。

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class BIOServer {public static void main(String[] args) throws IOException {// 服务端监听端口try (ServerSocket serverSocket = new ServerSocket(9090)) {System.out.println("<<服务端>> 等待连接中...");while (true) {// 监听与此 Socket 建立的连接并接受它。该方法阻塞,直到建立连接。Socket socket = serverSocket.accept();System.out.printf("<<服务端>> 收到来自%s的连接\n", socket.getRemoteSocketAddress());handler(socket);}}}//编写一个handler方法,和客户端通讯public static void handler(Socket socket) throws IOException {byte[] bytes = new byte[1024];// 通过socket获取输入流InputStream inputStream = socket.getInputStream();// 循环的读取客户端发送的数据while (true) {int read = inputStream.read(bytes);if (read != -1) {String msg = new String(bytes, 0, read);System.out.printf("当前线程id = %s,线程名字=%s。", Thread.currentThread().getId(), Thread.currentThread().getName());System.out.printf("接受到来自%s的消息:", socket.getRemoteSocketAddress());System.out.println(msg);} else {System.out.printf("关闭和%s的连接\n", socket.getRemoteSocketAddress());break;}}}
}

客户端代码,使用Socket连接服务端。

import java.io.*;
import java.net.Socket;
import java.util.Scanner;public class BIOClient {public static void main(String[] args) throws IOException {Socket socket = new Socket("localhost", 9090);System.out.printf("当前 <<客户端>> 地址为%s\n", socket.getLocalSocketAddress());System.out.print("请输入内容,发送到服务端 (输出“exit”时退出):");Scanner scanner = new Scanner(System.in);while(scanner.hasNext()) {String msg = scanner.nextLine();if ("exit".equalsIgnoreCase(msg)) {socket.close();break;}OutputStream outputStream = socket.getOutputStream();outputStream.write(msg.getBytes());System.out.print("请输入内容,发送到服务端 (输出“exit”时退出):");}}
}

测试:先启动服务端,再启动两个客户端,分别发送消息。

发现只有第一个客服端连接到服务端,其实这时第二个客户端已经建立连接,但是因为BIO模型,服务端只能处理一个连接,当关闭第一个客户端后,第二个客户端的消息就会马上发送到服务端了。

🌈NIO

NIO的三大核心组件关系图

一个线程关联一个Selector。

一个Selector关联多个Channel,Selector根据不同的事件在各个Channel中切换。

Channel通过Buffer在服务端和客户端之间进行数据交换。

🏍Buffer

Buffer对象本质上是一个可读写数据的内存块,一个容器(数组)。

Buffer不同于BIO中流,BIO中同一个流只能进行写或者读的操作。但是Buffer既可以写入数据也可以读取数据

Buffer种类有以下几种,其中使用较多的是ByteBuffer

核心属性

// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
  • capacity:缓冲区的容量。通过构造函数赋予,一旦设置,无法更改
  • limit:缓冲区的界限。位于limit 后的数据不可读写。缓冲区的限制不能为负,并且不能大于其容量
  • position下一个读写位置的索引(类似PC)。缓冲区的位置不能为负,并且不能大于limit
  • mark:记录当前position的值。position被改变后,可以通过调用reset() 方法恢复到mark的位置。

核心方法

put(T obj):插入数据,同时position往后移动。

flip():读写模式的切换。本质是改变核心属性的值。

get():读取缓存区中的一个值,同时position往后移动。

get(int index):读取指定位置的值,但是position不会变。

rewind():只在读模式下使用,恢复position、limit和capacity的值,变为进行get()前的值

clean():将缓冲区的属性恢复最初的状态,达到删除的效果,此时原数据还在,下次写会覆盖。

mark():将position的值保存到mark属性。

reset():将mark属性的值给position

compact():ByteBuffer类的方法。把position之前的数据清空,把剩余的数据往前移动。

🎗Channel

  • Channel不同于BIO中流,Channel可以读写,但流只能读或只能写。
  • Channel与Buffer紧密结合,数据总是从Channel读入Buffer,从Buffer写入Channel。
  • 常见的通道类型包括FileChannel(用于文件I/O)、SocketChannel(用于网络I/O)和ServerSocketChannel(用于服务器端网络I/O)。

🎈Selector

Selector能够检测多个注册的Channel上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的处理。这样就可以只用一个单线程去管理多个Channel,也就是管理多个连接和请求。

核心方法

public abstract class Selector implements Closeable {// 创建一个Selector并返回public static Selector open() throws IOException;// Selector是否打开public abstract boolean isOpen();// 返回创建Selector的提供者public abstract SelectorProvider provider();// 返回的Selector的 key 集合public abstract Set<SelectionKey> keys();// 返回Selector选择过的key集合public abstract Set<SelectionKey> selectedKeys();// Selector立即执行选择操作,返回已选择的key的数量// 选择操作:对注册进入Selector中的Channel(准备进行IO操作)// 放到内部的一个集合中。public abstract int selectNow() throws IOException;// Selector阻塞执行选择操作,直到有可以选择的Channel// 或者阻塞时间超过timeout,才会返回。public abstract int select(long timeout) throws IOException;// Selector阻塞执行选择操作,直到有可以选择的Channel才会返回。public abstract int select() throws IOException;// 使尚未返回的第一个选择操作立即返回。public abstract Selector wakeup();// 关闭Selectorpublic abstract void close() throws IOException;
}

🧨实战demo

验证在NIO模型下,服务端一个线程能处理多个客户端连接。

public class NIOServer {public static void main(String[] args) throws Exception {try(Selector selector = Selector.open();ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {serverSocketChannel.bind(new InetSocketAddress(9090));serverSocketChannel.configureBlocking(false);serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("<<服务端>> 等待连接中...");while(true) {// (阻塞)选择已准备好进行IO操作的Channel对应的key集合int count = selector.select();if(count > 0) {// 返回前面选择的key集合,select()必须在selectedKeys()之前调用,否则没有选择key,那么selectedKeys()就没有数据Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while(iterator.hasNext()) {SelectionKey selectionKey = iterator.next();iterator.remove();// 测试key对应的Channel是否准备好接受一个新的Socket连接if(selectionKey.isAcceptable()) {// 拿到Socket连接SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);// 注册进入SelectorsocketChannel.register(selector, SelectionKey.OP_READ);System.out.printf("<<服务端>> 收到来自%s的连接\n", socketChannel.getRemoteAddress());} else if (selectionKey.isReadable()) { // 测试key的通道是否已准备好读取。readData(selectionKey);}}}}}}private static void readData(SelectionKey selectionKey) throws IOException {//拿到key关联的SocketChannelSocketChannel socketChannel = (SocketChannel) selectionKey.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 把Channel中数据写入Bufferint count = socketChannel.read(byteBuffer);if(count > 0) {// 反转Buffer,切换Buffer的读写模式byteBuffer.flip();String msg = new String(byteBuffer.array(), 0, byteBuffer.limit());System.out.printf("当前线程id = %s,线程名字=%s。", Thread.currentThread().getId(), Thread.currentThread().getName());System.out.printf("接受到来自%s的消息:", socketChannel.getRemoteAddress());System.out.println(msg);} else if (count == -1) {System.out.printf("关闭和%s的连接\n", socketChannel.getRemoteAddress());selectionKey.cancel();socketChannel.close();}}
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;public class NIOClient {public static void main(String[] args) throws IOException {SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 9090));Scanner scanner = new Scanner(System.in);System.out.printf("当前 <<客户端>> 地址为%s\n", socketChannel.getLocalAddress());System.out.print("请输入内容,发送到服务端 (输出“exit”时退出):");while (scanner.hasNext()) {String msg = scanner.nextLine();if ("exit".equalsIgnoreCase(msg)) {socketChannel.close();break;}socketChannel.write(ByteBuffer.wrap(msg.getBytes()));System.out.print("请输入内容,发送到服务端 (输出“exit”时退出):");}}
}

测试:先启动服务端,再启动两个客户端,分别发送消息。

发现两个客户端可以同时连接到服务端,同时发送消息。

🎨粘包与半包

粘包:发送方在发送数据时,并不是一条一条地发送数据,而是将数据整合在一起,当数据达到一定的数量后再一起发送。这就会导致多条信息被放在一个缓冲区中被一起发送出去

半包:接收方的缓冲区的大小是有限的,当接收方的缓冲区满了以后,就需要将信息截断,等缓冲区空了以后再继续放入数据。这就会发生一段完整的数据最后被截断的现象

相关文章:

Netty入门基础:IO模型中BIO\NIO概念及区别【附演示代码】

文章目录 &#x1f600;BIO&#x1f4a2;实战demo &#x1f308;NIO&#x1f3cd;Buffer核心属性核心方法 &#x1f397;Channel&#x1f388;Selector核心方法 &#x1f9e8;实战demo &#x1f3a8;粘包与半包 &#x1f600;BIO 传统IO模型&#xff0c;同步阻塞&#xff0c;每…...

vue2 使用环境变量

一. 在根目录下创建.env.xxx文件 .env 基础系统变量&#xff0c;无论何种环境&#xff0c;都可使用其中配置的值&#xff0c;其他环境中的变量会覆盖.env中的同名变量。 .env.development 开发环境 .env.production 生产环境 .env.staging 测试环境 二. 内容格式 vue2 使用是以…...

数据预处理

继续提取代码片段&#xff1a; 12. **导入iris数据集并查看前5行数据**&#xff1a; python from sklearn.datasets import load_iris iris load_iris() X iris.data print(iris数据集的维度为:, X.shape) print(iris数据集的前5行数据为:\n, X[:5]) …...

django宠物领养管理系统-计算机毕业设计源码26858

目录 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 系统总体设计 3…...

使用TeamViewer远程局域网内的两台电脑

有个场景&#xff0c;有人还不知道TV可以局域网操作&#xff0c;记录一下。 主要就是修改设置&#xff0c;将取消激活改为接受 然后输入受控端的ip即可...

GUI简介、Swing的常用组件、java程序的运行过程、class文件、JAR、runable_jar、双括号初始化

GUI简介 GUI&#xff1a;图形用户界面&#xff0c;在计算机中采用图形的方式显示用户界面 java的GUI开发 AWT&#xff1a;java最早推出的GUI编程开发包&#xff0c;界面风格跟随操作系统SWT&#xff1a;eclipse就是java使用SWT开发的Swing&#xff1a;在AWT的基础上扩充了功能…...

@Autowired和@Resource和getBean()区别

今天遇到一个对我来说很奇葩的错误&#xff0c;我想在Service中注入bean&#xff0c;我这里使用了Autowired和Resource都不能注入&#xff0c;导致初始化失败&#xff0c;使用了getBean()方法就可以注入。从来没有遇到过这个问题。后来我查询了一下&#xff0c;才明白了原理。我…...

Merlion笔记(四):添加一个新的预测模型

文章目录 1 模型配置类2 模型类3 运行模型&#xff1a;一个简单的例子4 可视化5 定量评估6 定义一个基于预测器的异常检测器 本文提供了一个示例&#xff0c;展示如何向 Merlion 添加一个新的预测模型,遵循 CONTRIBUTING.md 中的说明。建议在阅读本篇文章之前&#xff0c;先查…...

【论文阅读】ESRGAN

学习资料 论文题目&#xff1a;增强型超分辨率生成对抗网络&#xff08;ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks&#xff09;论文地址&#xff1a;[1809.00219] ESRGAN&#xff1a;增强型超分辨率生成对抗网络代码&#xff1a;xinntao / ESRGAN&am…...

电脑异常情况总结

文章目录 笔记本无症状息屏黑屏 笔记本无症状息屏黑屏 &#x1f34e; 问题描述&#xff1a; 息屏导致黑屏&#xff1b;依次操作计算机--》右键--》管理--》事件查看器--》Windows日志--》系统&#xff1b;从息屏到异常黑屏之间出现了很多错误&#xff0c;如下&#xff1a;事件…...

[项目详解][boost搜索引擎#1] 概述 | 去标签 | 数据清洗 | scp

目录 一、前言 二、项目的相关背景 三、搜索引擎的宏观原理 四、搜索引擎技术栈和项目环境 五、正排索引 VS 倒排索引--原理 正排索引 分词 倒排索引 六、编写数据去除标签和数据清洗模块 Parser 1.数据准备 parser 编码 1.枚举文件 EnumFile 2.去标签ParseHtml(…...

PL/I语言的起源?有C语言,有B语言和A语言吗?为什么shell脚本最开始可能有#!/bin/bash字样?为什么不支持嵌套注释?

PL/I语言的起源 在20世纪50~60年代&#xff0c;当时主流的编程语言是COBOL/FORTRAN/ALGOL等&#xff0c;IBM想要设计一门通用的编程语言&#xff0c;已有的编程语言无法实现此要求&#xff0c;故想要设计一门新语言&#xff0c;即是PL/I. PL/I是Programming Language/One的缩写…...

gin入门教程(3):创建第一个 HTTP 服务器

首先设置golang github代理&#xff0c;可解决拉取git包的时候&#xff0c;无法拉取的问题&#xff1a; export GOPROXYhttps://goproxy.io再查看自己的go版本&#xff1a; go version我这里的版本是&#xff1a;go1.23.2 linux/arm64 准备工作做好之后就可以进行开发了 3.…...

Vue+ECharts+iView实现大数据可视化大屏模板

Vue数据可视化 三个大屏模板 样式还是比较全的 包括世界地图、中国地图、canvas转盘等 项目演示&#xff1a; 视频&#xff1a; vue大数据可视化大屏模板...

el-table 表格设置必填项

el-table 表格设置必填项 要在 el-table 中集成 el-form 来设置必填项&#xff0c;并进行表单验证&#xff0c;可以使用 Element UI 提供的表单验证功能。下面是一个详细的示例&#xff0c;展示了如何在 el-table 中使用 el-form 来设置必填项&#xff0c;并进行验证。 示例代…...

vivo 轩辕文件系统:AI 计算平台存储性能优化实践

在早期阶段&#xff0c;vivo AI 计算平台使用 GlusterFS 作为底层存储基座。随着数据规模的扩大和多种业务场景的接入&#xff0c;开始出现性能、维护等问题。为此&#xff0c;vivo 转而采用了自研的轩辕文件系统&#xff0c;该系统是基于 JuiceFS 开源版本开发的一款分布式文件…...

Vue学习笔记(四)

事件处理 我们可以使用 v-on 指令 (通常缩写为 符号) 来监听 DOM 事件&#xff0c;并在触发事件时执行一些 JavaScript。用法为 v-on:click"methodName" 或使用快捷方式 click"methodName" 事件处理器的值可以是&#xff1a; 内联事件处理器&#xff1…...

发送短信,验证码

短信 注册阿里云的账号 开通短信服务 测试短信服务是否可用 导入jar <!-- 短信相关 --><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.6.0</version><…...

国内大语言模型哪家更好用?

大家好&#xff0c;我是袁庭新。 过去一年&#xff0c;AI大语言模型在爆发式增长&#xff0c;呈现百家争鸣之态。国内外相关厂商积极布局&#xff0c;并相继推出自家研发的智能化产品。 我在工作中已习惯借助AI来辅助完成些编码、创作、文生图等任务&#xff0c;甚至对它们产…...

OTP一次性密码、多因子认证笔记

文章目录 双因子认证(多因子认证)otp算法(ONE-TIME PASSWORD)otp算法大概分为几部 otp的机制服务端客户端(app端)两种主流算法otp流程图 otp是通用的吗 手机验证码天天在用&#xff0c;但是居然不知道这个是otp&#xff0c;伤自尊了&#xff0c;必须弄清原理。 先要知道几个概念…...

玉米生长阶段检测系统源码&数据集全套:改进yolo11-dysample

改进yolo11-DLKA等200全套创新点大全&#xff1a;玉米生长阶段检测系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.10.24 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片或者视…...

【机器学习】决策树算法

目录 一、决策树算法的基本原理 二、决策树算法的关键概念 三、决策树算法的应用场景 四、决策树算法的优化策略 五、代码实现 代码解释&#xff1a; 在机器学习领域&#xff0c;决策树算法是一种简单直观且易于理解的分类和回归方法。它通过学习数据特征和决策规则&#…...

P2818 天使的起誓

天使的起誓 题目描述 Tenshi 非常幸运地被选为掌管智慧之匙的天使。在正式任职之前&#xff0c;她必须和其他新当选的天使一样要宣誓。 宣誓仪式是每位天使各自表述自己的使命&#xff0c;他们的发言稿放在 n n n 个呈圆形排列的宝盒中。这些宝盒按顺时针方向被编上号码 1…...

数字信号处理实验简介

数字信号处理(Digital Signal Processing,简称DSP)是电子工程、通信、计算机科学等领域中的一个重要分支,它涉及到对离散时间信号进行分析、处理和合成的理论和方法。数字信号处理课程的实验环节通常旨在帮助学生将理论知识应用于实际问题中,通过实践加深对DSP概念和技术的…...

Flask-SQLAlchemy 组件

一、ORM 要了解 ORM 首先了解以下概念。 什么是持久化 持久化 (Persistence)&#xff0c;即把数据&#xff08;如内存中的对象&#xff09;保存到可永久保存的存储设备中&#xff08;如磁盘&#xff09;。持久化的主要应用是将内存中的数据存储在关系型的数据库中&#xff0c;…...

Could not retrieve mirrorlist http://mirrorlist.centos.org错误解决方法

文章目录 背景解决方法 背景 今天在一台新服务器上安装nginx&#xff0c;在这个过程中需要安装相关依赖&#xff0c;在使用yum install命令时&#xff0c;发生了以下报错内容&#xff1a; Could not retrieve mirrorlist http://mirrorlist.centos.org/?release7&archx8…...

最新PHP网盘搜索引擎系统源码 附教程

最新PHP网盘搜索引擎系统源码 附教程&#xff0c;这是一个基于thinkphp5.1MySQL开发的网盘搜索引擎&#xff0c;可以批量导入各大网盘链接&#xff0c;例如百度网盘、阿里云盘、夸克网盘等。 功能特点&#xff1a;网盘失效检测&#xff0c;后台管理功能&#xff0c;网盘链接管…...

SpringBoot面试热题

1.Spring IOC(控制反转)和AOP(面相切面编程)的理解 控制反转意味着将对象的控制权从代码中转移到Spring IOC容器。 本来是我们自己手动new出来的对象&#xff0c;现在则把对象交给Spring的IOC容器管理&#xff0c;IOC容器作为一个对象工厂&#xff0c;管理对象的创建和依赖关系…...

ASP.NET Core8.0学习笔记(二十三)——EF Core自引用

一、什么是自引用 1.在常见的树状目录中&#xff0c;其结构如下&#xff1a; 每一个菜单可能有父级菜单&#xff0c;也可能有子菜单。但是无论是哪一级菜单&#xff0c;他们都是同属于菜单对象。将这个菜单对象使用代码进行描述&#xff1a; 在上面的代码中&#xff0c;主…...

springboot童装销售管理系统-计算机毕业设计源码92685

摘 要 童装销售管理系统是为童装店商家提供的在线销售管理系统&#xff0c;本系统的研发设计能够增加童装店商家的童装宣传和推广&#xff0c;提升客流量和订单量&#xff0c;增加商家的营业收益。原有的童装品销售系统管理采用手工管理的方式&#xff0c;各种童装品宣传和订单…...

有用模板网在线制作免费网站/杭州seo搜索引擎优化公司

前言 千万级大表如何优化&#xff0c;这是一个很有技术含量的问题&#xff0c;通常我们的直觉思维都会跳转到拆分或者数据分区。除此之外&#xff0c;还有其他的思路和解决方案。根据本人多年的工作经验&#xff0c;做了如下总结。 方案 "千万级大表优化"这句话有…...

北京商地网站建设公司/微软优化大师

SimpleAdapter是ArrayList和 ListView的桥梁。这个ArrayList里边的每一项都是一个Map<String,?>类型。 ArrayList当中的每一项 Map对象都和ListView里边的每一项进行数据绑定一一对应。 SimpleAdapter的构造函数&#xff1a; SimpleAdapter(Context context, List<?…...

江苏泰州建设局网站/友情链接买卖代理

在这篇文章里&#xff0c;你将学会什么是函数范式以及如何使用Python进行函数式编程。你也将了解列表推导和其它形式的推导。函数范式在命令式范式中&#xff0c;通过为计算机提供一系列指令然后执行它们来完成任务。在执行这些指令时&#xff0c;可以改变某些状态。例如&#…...

网站平台优化/百度客服号码

第2课-数据的艺术 数据结构起源(1) 计算机从解决数值计算问题到解决生活中的问题。 (2) 现实生活中的问题涉及不同个体间的复杂联系。 (3) 需要在计算机程序中描述生活中个体间的。 数据结构主要研究非数值计算程序问题中的操作对象以及它们之间的关系。 关键概念&#xff08;1…...

坑梓网站建设咨询/长沙网站制作主要公司

方法一&#xff1a;删除注册表选项&#xff0c;变为评估模式 1.windowR打开管理 2.输入regedit后回车&#xff0c;则就会打开注册表编辑器 3.里面有一个cacaheId然后删掉&#xff0c;果断的删掉&#xff0c;然后重新打开即可 方法二&#xff1a;删除动态库文件 4.找到Beyo…...

怎么做网页模板展示网站/网络营销的六大功能

认识gtest工具后&#xff0c;关于它的使用&#xff0c;下面将用一个demo程序演示一下gtest的用法以及成果展示。 一、需要测试的C代码&#xff1a; #include "myfunction.h"//计算和的函数 int add(int a, int b) {int c a b;return c; }//计算最小公约数 int Foo(…...