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

9.NIO非阻塞式网络通信入门


highlight: arduino-light

Selector 示意图和特点说明

一个 I/O 线程可以并发处理 N 个客户端连接和读写操作,这从根本上解决了传统同步阻塞 I/O 一连接一线程模型。架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。

image.png

服务端流程

  • 1、当客户端连接服务端时,服务端会通过 ServerSocketChannel 得到 SocketChannel:获取通道

    java ServerSocketChannel ssChannel = ServerSocketChannel.open();

  • 2、切换非阻塞模式

    java ssChannel.configureBlocking(false);

  • 3、绑定连接

    java ssChannel.bind(new InetSocketAddress(9999));

  • 4、 获取选择器

    java Selector selector = Selector.open();

  • 5、 将通道注册到选择器上, 并且指定“监听接收事件”

    java ssChannel.register(selector, SelectionKey.OP_ACCEPT);

    1. 轮询式的获取选择器上已经“准备就绪”的事件
  • java //轮询式的获取选择器上已经“准备就绪”的事件,大于0 说明存在 准备就绪的事件 while (selector.select() > 0) {        System.out.println("轮一轮");        //7. 获取当前选择器中所有注册的“选择键(已就绪的监听事件)”        Iterator<SelectionKey> it = selector.selectedKeys().iterator();        while (it.hasNext()) {            //8. 获取准备“就绪”的是事件            SelectionKey sk = it.next();            //9. 判断具体是什么事件准备就绪            if (sk.isAcceptable()) {                //10. 若“接收就绪”,获取客户端连接                SocketChannel sChannel = ssChannel.accept();                //11. 切换非阻塞模式                sChannel.configureBlocking(false);                //12. 将该通道注册到选择器上并修改注册事件为read                sChannel.register(selector, SelectionKey.OP_READ);           } else if (sk.isReadable()) {                //13. 获取当前选择器上“读就绪”状态的通道                SocketChannel sChannel = (SocketChannel) sk.channel();                //14. 读取数据                ByteBuffer buf = ByteBuffer.allocate(1024);                int len = 0;                while ((len = sChannel.read(buf)) > 0) {                    buf.flip();                    System.out.println(new String(buf.array(), 0, len));                    buf.clear();               }           }            //15. 处理完毕 移除选择键 SelectionKey            it.remove();       }   } }

客户端流程

1.获取通道

java SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));

2.切换非阻塞模式

java sChannel.configureBlocking(false);

3.分配指定大小的缓冲区

java ByteBuffer buf = ByteBuffer.allocate(1024);

4.发送数据给服务端

java Scanner scan = new Scanner(System.in); while(scan.hasNext()){ String message = scan.nextLine(); buf.put((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(System.currentTimeMillis()) + "\n" + message).getBytes()); buf.flip(); sChannel.write(buf); buf.clear(); } //关闭通道 sChannel.close();

NIO非阻塞式网络通信入门案例

需求:服务端接收客户端的连接请求,并接收多个客户端发送过来的事件。

代码案例

java /**  客户端 */ public class Client { ​ public static void main(String[] args) throws Exception { //1. 获取通道 - SelectionKey.OP_ACCEPT 对应监听接收事件 SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999)); //2. 切换非阻塞模式 sChannel.configureBlocking(false); //3. 分配指定大小的缓冲区 ByteBuffer buf = ByteBuffer.allocate(1024); //4. 发送数据给服务端 Scanner scan = new Scanner(System.in);         while(scan.hasNext()){ String message = scan.nextLine(); buf.put((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(System.currentTimeMillis()) + "\n" + message).getBytes()); buf.flip();            //客户端写对应服务器端读就绪 sChannel.write(buf); buf.clear(); } //5. 关闭通道 sChannel.close(); } } ​ /** 服务端 */ public class Server {    public static void main(String[] args) throws IOException {        //1. 获取通道        ServerSocketChannel ssChannel = ServerSocketChannel.open();        //2. 切换非阻塞模式        ssChannel.configureBlocking(false);        //3. 绑定连接        ssChannel.bind(new InetSocketAddress(9999));        //4. 获取选择器        Selector selector = Selector.open();        //5. 将通道注册到选择器上, 并且指定“监听接收事件”        ssChannel.register(selector, SelectionKey.OP_ACCEPT);        //6. 轮询式的获取选择器上已经“准备就绪”的事件        while (selector.select() > 0) {            System.out.println("轮一轮");            //7. 获取当前选择器中所有注册的“选择键(已就绪的监听事件)”            Iterator<SelectionKey> it = selector.selectedKeys().iterator();            while (it.hasNext()) {                //8. 获取准备“就绪”的是事件                SelectionKey sk = it.next();                //9. 判断具体是什么事件准备就绪                if (sk.isAcceptable()) {                    //10. 若“接收就绪”,获取客户端连接                    SocketChannel sChannel = ssChannel.accept();                    //11. 切换非阻塞模式                    sChannel.configureBlocking(false);                    //12. 将该通道注册到选择器上 这里可以把缓存指定上                    sChannel.register(selector, SelectionKey.OP_READ);               } else if (sk.isReadable()) {                    //13. 获取当前选择器上“读就绪”状态的通道                    SocketChannel sChannel = (SocketChannel) sk.channel();                    //14. 读取数据                    ByteBuffer buf = ByteBuffer.allocate(1024);                    int len = 0;                    while ((len = sChannel.read(buf)) > 0) {                        buf.flip();                        System.out.println(new String(buf.array(), 0, len));                        buf.clear();                   }               }                //15. 取消选择键 SelectionKey                it.remove();           }       }   } }

尚硅谷代码案例

```java package com.atguigu.nio;

import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator;

//网络服务器端程序 public class NIOServer { public static void main(String[] args) throws Exception{ //1. 得到一个ServerSocketChannel对象 ServerSocketChannel serverSocketChannel=ServerSocketChannel.open(); //2. 得到一个Selector对象 Selector selector=Selector.open(); //3. 绑定一个端口号, 在服务器的6666监听 2个方式有什么区别 //serverSocketChannel.bind(new InetSocketAddress(6666)); serverSocketChannel.socket().bind(new InetSocketAddress(6666)); //4. 设置非阻塞方式 serverSocketChannel.configureBlocking(false); //5. 把ServerSocketChannel对象注册给Selector对象 serverSocketChannel.register(selector, SelectionKey.OPACCEPT); //6. 干活 while(true){ //6.1 监控客户端 //如果使用 selector.select() 就会阻塞在这里的 if(selector.select(1000)==0){ //nio非阻塞式的优势 System.out.println("Server:等待了1秒,无客户端连接"); continue; } //6.2 得到SelectionKey,判断通道里的事件 Iterator keyIterator=selector.selectedKeys().iterator(); while(keyIterator.hasNext()){ SelectionKey key=keyIterator.next(); if(key.isAcceptable()){ //客户端连接请求事件 SocketChannel socketChannel=serverSocketChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector,SelectionKey.OP READ, ByteBuffer.allocate(1024)); } if(key.isReadable()){ //读取客户端数据事件 SocketChannel channel=(SocketChannel) key.channel(); ByteBuffer buffer=(ByteBuffer) key.attachment(); channel.read(buffer); System.out.println("接收到客户端数据:"+new String(buffer.array())); } // 6.3 手动从集合中移除当前key,防止重复处理 keyIterator.remove(); } } }

} ```

```java package com.atguigu.nio; ​ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; ​ public class NIOClient {    public static void main(String[] args) throws Exception{ ​        //得到一个网络通道        SocketChannel socketChannel = SocketChannel.open();        //设置非阻塞        socketChannel.configureBlocking(false);        //提供服务器端的ip 和 端口        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);        //连接服务器        if (!socketChannel.connect(inetSocketAddress)) {            while (!socketChannel.finishConnect()) {                System.out.println("因为连接需要时间,客户端不会阻塞,可以做其它工作..");           }       } ​        //...如果连接成功,就发送数据        String str = "hello, 尚硅谷~";        //Wraps a byte array into a buffer        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());        // 发送数据,将 buffer 数据写入 channel        socketChannel.write(buffer);        System.in.read(); ​   } }

```

使用6666端口有个坑爹的地方,端口号被占用。

解决:https://www.cnblogs.com/jf-67/p/8425405.html

原因:https://blog.csdn.net/hi_pig2003/article/details/52995528

相关文章:

9.NIO非阻塞式网络通信入门

highlight: arduino-light Selector 示意图和特点说明 一个 I/O 线程可以并发处理 N 个客户端连接和读写操作&#xff0c;这从根本上解决了传统同步阻塞 I/O 一连接一线程模型。架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。 服务端流程 1、当客户端连接服务端时&…...

QT基于TCP协议实现数据传输以及波形绘制

这个玩意我做了两个&#xff0c;一个是安卓app&#xff0c;一个是Windows程序。代码并非全部都是由我从无到有实现&#xff0c;只是实现了我想要的功能。多亏了巨人的肩膀&#xff0c;开源万岁&#xff01;&#xff01;&#xff01; 我把程序放到GitHub上&#xff0c;需要的可…...

苹果safari浏览器播放不了video标签视频

今天遇到了个神奇的问题&#xff0c;视频文件在pc端和安卓手机上播放都没问题&#xff0c;但是在ios上就是播放不了&#xff0c;大概代码如下&#xff1a; 前端代码&#xff1a; <video id"video" width"350" height"500" controls><s…...

【粒子群算法和蝴蝶算法组合】粒子群混沌混合蝴蝶优化算法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Java设计模式之单例模式详解(懒汉式和饿汉式)

在开发工作中&#xff0c;有些类只需要存在一个实例&#xff0c;这时就可以使用单例模式。Java中的单例模式是一种常见的设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供全局访问点。下面来介绍一下两种常见的单例模式&#xff1a;懒汉式和饿汉式。 一、懒汉式…...

软件测试基本知识

安全测试 安全防护策略&#xff1f;&#xff08;漏洞扫描、入侵检查、安全日志、隔离防护&#xff09; 安全日志&#xff1a;用于记录非法用户的登录名称、操作时间及内容等信息&#xff0c;以便发现问题并提出解决措施&#xff1b;安全日志仅记录相关信息&#xff0c;不对非…...

Vue项目中强制刷新页面的方法

我们在动态切换组件的过程中&#xff0c;导航栏和底栏不动&#xff0c;动态切换中间区域的情况&#xff0c;在首页可以进行跳转任意组件&#xff0c;在组件与组件之间不能相互跳转&#xff0c;路由发生了变化&#xff0c;但是页面未改变&#xff0c;这时我们就需要强制刷新页面…...

文件按关键字分组-切割-染色-写入excel

1. 背景 针对下面的文件data.csv&#xff0c;首先根据fid进行排序&#xff0c;然后分组&#xff0c;使相同fid的记录放到同一个excel文件中&#xff0c;并对每列重复的数据元素染上红色。 fid,user_id -1000078398032092029,230410010036537520 -1000078398032092029,23042301…...

爬虫的基本原理:爬虫概述及爬取过程

前言 随着互联网的不断发展和普及&#xff0c;我们的生活越来越离不开网络。而网络世界中有着海量的信息和数据&#xff0c;这些信息和数据对于我们的工作和生活都有很大的帮助。但是&#xff0c;如何高效地获取这些数据呢&#xff1f;这时候&#xff0c;爬虫这个工具就派上用…...

cocos2D插件转3D插件

cocos2D插件转3D插件 use strict;/*** 3d插件api映射,兼容2d插件* */let fs require("fs");let path require("path");let baseDir ;const prsPath (Editor.Project && Editor.Project.path ? Editor.Project.path : Editor.remote.projectP…...

[Angular] 主从表结构,从表记录在主表固定栏位上呈现

Background 主从表结构&#xff0c;有时为了方便数据呈现&#xff0c;在UI上不显示从表资料&#xff0c;那么需要动态把从表的资料加载到主表的固定栏位上。 例如&#xff1a;主表是人员信息&#xff0c;从表是银行卡信息&#xff0c;一个人在同一家银行可能有多张银行卡&…...

Kotlin Multiplatform 创建多平台分发库

目标&#xff1a;通过本教程学习如何使用 Kotlin Multiplatform Library 创建多平台分发库(iOS&#xff0c;安卓)。 创建一个项目 1、本教程使用的是Android Studio创建 2、选择 新建工程&#xff0c;选择 Kotlin Multiplatform Library 3、点击next 输入需要创建的项目名称以…...

[SQL挖掘机] - union/union all 使用注意事项

因为当使用union和union all操作符时&#xff0c;有一些注意事项需要考虑&#xff1a; 1. 列数和数据类型匹配&#xff1a; 要使用union或union all合并结果集&#xff0c;两个或多个查询的 select 语句必须返回相同数量和类型的列。确保每个查询返回相同的列数&#xff0c;并…...

php 单例模式

1&#xff0c;单例模式&#xff0c;属于创建设计模式&#xff0c;简单来说就是一个类只能有一个实例化对象&#xff0c;并提供一个当前类的全局唯一可访问入口&#xff1b; 2&#xff0c;例子 <?phpclass Singleton {private static $instance null;// 禁止被实例化priva…...

【数据结构】实验二:顺序表

实验二 顺序表 一、实验目的与要求 1&#xff09;熟悉顺序表的类型定义&#xff1b; 2&#xff09;熟悉顺序表的基本操作&#xff1b; 3&#xff09;灵活应用顺序表解决具体应用问题。 二、实验内容 1&#xff09;在一个整数序列a1,a2,…,an中&#xff0c;若存在一个数&…...

【高级数据结构】线段树

目录 最大数&#xff08;单点修改&#xff0c;区间查询&#xff09; 线段树1&#xff08;区间修改&#xff0c;区间查询&#xff09; 最大数&#xff08;单点修改&#xff0c;区间查询&#xff09; 洛谷&#xff1a;最大数https://www.luogu.com.cn/problem/P1198 题目描述 …...

qt简易闹钟

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);ui->stopBtn->setDisabled(true);this->setFixedSize(this->size()); //设置固定大小this->s…...

python和c加加有什么区别,c和c++和python先学哪个

本篇文章给大家谈谈c加加编程和python编程有什么区别&#xff0c;以及python和c加加有什么区别&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 1、python和c学哪个好 学C好。 C通常比Python更快&#xff0c;因为C是一种编译型语言&#xff0c;而Python则是…...

Visual Studio 2022 cmake配置opencv开发环境

1. 环境与说明 这里我用的是 widnows 10 64位&#xff0c;Visual Studio 用的 Visual Studio Community 2022 (社区版) 对于Android开发工程师来说&#xff0c;为什么要使用Visual Studio 呢 ? 因为在Visual Studio中开发调试OpenCV方便&#xff0c;可以开发调试好后&#xf…...

C++ GDAL找出多时相遥感影像缺失的日期并自动生成新的全零图像作为替补

本文介绍基于C 语言的GDAL库&#xff0c;基于一个存储大量遥感影像的文件夹&#xff0c;依据每一景遥感影像的文件名中表示日期的那个字段&#xff0c;找出这些遥感影像中缺失的成像日期&#xff0c;并新生成多个像元值全部为0的栅格文件&#xff0c;作为这些缺失日期当日的遥感…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

TJCTF 2025

还以为是天津的。这个比较容易&#xff0c;虽然绕了点弯&#xff0c;可还是把CP AK了&#xff0c;不过我会的别人也会&#xff0c;还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...

Python爬虫实战:研究Restkit库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...

C++ 类基础:封装、继承、多态与多线程模板实现

前言 C 是一门强大的面向对象编程语言&#xff0c;而类&#xff08;Class&#xff09;作为其核心特性之一&#xff0c;是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性&#xff0c;包括封装、继承和多态&#xff0c;同时讨论类中的权限控制&#xff0c;并展示如何使用类…...