NIO通信代码示例

news2025/1/11 18:38:07

NIO通信架构图

在这里插入图片描述

1.Client

NioClient

package nio;

import constant.Constant;

import java.io.IOException;
import java.util.Scanner;

public class NioClient {
    
    private static NioClientHandle nioClientHandle;
    
    public static void start() {
        nioClientHandle = new NioClientHandle(Constant.DEFAULT_SERVER_IP, Constant.DEFAULT_PORT);
        
        new Thread(nioClientHandle, "Client").start();
    }
    
    // 向服务器发送消息
    public static boolean sendMsg(String msg) throws IOException {
        nioClientHandle.sendMsg(msg);
        return true;
    }

    public static void main(String[] args) throws IOException {
        start();
        Scanner scanner = new Scanner(System.in);
        while (NioClient.sendMsg(scanner.next()));
    }
}

NioClientHandle

package nio;

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.util.Iterator;
import java.util.Set;

public class NioClientHandle implements Runnable {
    
    private String host;
    
    private int port;
    
    private volatile boolean started;
    
    private Selector selector;
    
    private SocketChannel socketChannel;
    
    public NioClientHandle(String ip, int port) {
        this.host = ip;
        this.port = port;
        
        try {
            // 创建选择器的实例
             selector = Selector.open();
             // 创建ServerSocketChannel的实例
             socketChannel = SocketChannel.open();
             // 设置通道为非阻塞模式
            socketChannel.configureBlocking(false);
            
            started = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void stop() {
        started = false;
    }
    @Override
    public void run() {
        try {
            doConnect();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
        
        
        // 循环遍历selector
        while (started) {
            try {
                // 无论是否有读写事件发生,selector每隔1s被唤醒一次
                selector.select(1000);
                // 获取当前有哪些事件可以使用
                Set<SelectionKey> keys = selector.selectedKeys();
                // 转换为迭代器
                Iterator<SelectionKey> it = keys.iterator();
                SelectionKey key = null;
                while (it.hasNext()) {
                    key = it.next();
                    // 我们必须受限将处理过的SelectionKey从选定的集合中删除
                    // 如果我们没有删除处理过的键,那么它仍然会在主集合中以一个激活的键
                    // 出现,这会导致我们尝试再次处理它
                    it.remove();
                    try {
                        handleInput(key);
                    } catch (Exception e) {
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
        
        // selector关闭后会自动释放里面管理的资源
        if (selector != null) {
            try {
                selector.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 具体的事件处理方法
     * @param key
     */
    private void handleInput(SelectionKey key) throws IOException {
        if (key.isValid()) {
            // 获得关心当前事件的channel
            SocketChannel sc = (SocketChannel)key.channel();
            // 连接事件
            if (key.isConnectable()) {
                if (sc.finishConnect()) {
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else {
                    System.exit(1);
                }
            }
            
            // 有数据可读事件
            if (key.isReadable()) {
                // 创建ByteBuffer,并开启一个1M的缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 读取数据码流 返回读取到的字节数
                int readBytes = sc.read(buffer);
                // 读取到字节 对字节进行编解码
                if (readBytes > 0) {
                    //将缓冲区当前的limit设置为position, position = 0;
                    // 用于后续对缓冲区的读取操作
                    buffer.flip();
                    // 根据缓冲区可读字节数创建字节数组
                    byte[] bytes = new byte[buffer.remaining()];
                    // 将缓冲区可读字节数组复制到新建的数组中
                    buffer.get(bytes);
                    String result = new String(bytes, "UTF-8");
                    System.out.println("客户端收到消息:" + result);
                    
                } else if (readBytes < 0) {
                    key.cancel();
                    sc.close();
                }

            }
        }
    }
    
    
    public void sendMsg(String msg) throws IOException {
        doWrite(socketChannel, msg);
        
    }

    private void doWrite(SocketChannel socketChannel, String request) throws IOException {
        // 将消息编码为字节数组
        byte[] bytes = request.getBytes();
        // 根据数组容量创建ByteBuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        // 将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        // flip操作
        writeBuffer.flip();
        // 发送缓冲区的字节数组
        // 关心事件和读写网络不冲突
        socketChannel.write(writeBuffer);
        
    }
    
    private void doConnect() throws IOException {
        // 非阻塞的连接
        if (socketChannel.connect(new InetSocketAddress(host, port))) {
            socketChannel.register(selector, SelectionKey.OP_READ);
        } else {
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
        }
    }
}

2.Server

NioServer

package nio;

import constant.Constant;

public class NioServer {
    
    private static NioServerHandle nioServerHandle;
    
    public static void main(String[] args) {
        nioServerHandle = new NioServerHandle(Constant.DEFAULT_PORT);
        new Thread(nioServerHandle, "Server").start();
    }
}

NioServerHandle

package nio;

import constant.Constant;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NioServerHandle implements Runnable {
    private volatile boolean started;
    
    private ServerSocketChannel serverSocketChannel;
    
    private Selector selector;
    
    public NioServerHandle(int port) {
        try {
            // 创建选择器实例
            selector = Selector.open();
            // 创建ServerSocketChannel的实例
            serverSocketChannel = ServerSocketChannel.open();
            // 设置通道为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            // 绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(port));
            // 注册事件,表示关心客户端连接
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            // 
            started = true;
            System.out.println("服务器已启动, 端口号为:" + port);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        while (started) {
            try {
                // 获取当前有哪些事件
                selector.select(1000);
                // 获取事件的集合
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    // 我们必须首先将处理过的SelectionKey 从选定的键集合中删除
                    // 如果我们没有删除处理过的键,那么它仍然会在主集合中以
                    // 一个激活的键出现,这回导致我们尝试再次处理它
                    iterator.remove();
                    handleInput(key);
                }
            } catch (Exception e) {
                
            }
        }
    }

    /**
     * 处理事件的发生
     * @param key
     */
    private void handleInput(SelectionKey key) throws IOException {
        if (key.isValid()) {
            // 处理新接入的客户端的请求
            if (key.isAcceptable()) {
                // 获取关心当前事件的Channel
                ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                // 接受连接
                SocketChannel sc = ssc.accept();
                System.out.println("=========建立连接=========");
                sc.configureBlocking(false);
                // 关注读事件
                sc.register(selector, SelectionKey.OP_READ);
            }
            
            // 处理对端的发送的数据
            if (key.isReadable()) {
                SocketChannel sc = (SocketChannel) key.channel();
                // 创建ByteBuffer, 开辟一个缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 从通道里读取数据,然后写入buffer
                int readBytes = sc.read(buffer);
                if (readBytes > 0) {
                    // 将缓冲区当前的limit设置为position, position = 0
                    // 用于后续对缓冲区的读取操作
                    buffer.flip();
                    // 根据缓冲区可读字节数创建字节数组
                    byte[] bytes = new byte[buffer.remaining()];
                    // 将缓冲区可读字节数组复制到新建的数组中
                    buffer.get(bytes);
                    String message = new String(bytes, "UTF-8");
                    System.out.println("服务器收到消息:" + message);
                    // 处理数据
                    String result = Constant.response(message);
                    // 发送应答消息
                    doWrite(sc, result);
                }
            }
        }
    }

    private void doWrite(SocketChannel sc, String response) throws IOException {
        byte[] bytes = response.getBytes();
        ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
        buffer.put(bytes);
        buffer.flip();
        sc.write(buffer);
    }
}

3.代码运行实例

先启动server再启动client
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1374925.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Spring Cloud】Sentinel流量限流和熔断降级的讲解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Spring Cloud》。&#x1f3af;&#x1f3af; &am…

网工内推 | 高级网工,H3C认证优先,朝九晚六,周末双休

01 万德 招聘岗位&#xff1a;高级网络工程师 职责描述&#xff1a; 1、项目交付&#xff1a;项目管理和交付&#xff0c;包括项目前期的规划、实施以及后期的运维支持、项目验收等。 2、技术支持&#xff1a;为客户及合作伙伴提供网上问题远程和现场支持&#xff1b;对公司内…

【C++】STL 算法 ⑥ ( 二元谓词 | std::sort 算法简介 | 为 std::sort 算法设置 二元谓词 排序规则 )

文章目录 一、二元谓词1、二元谓词简介2、 std::sort 算法简介3、 代码示例 - 为 std::sort 算法设置 二元谓词 排序规则 一、二元谓词 1、二元谓词简介 " 谓词 ( Predicate ) " 是一个 返回 布尔 bool 类型值 的 函数对象 / 仿函数 或 Lambda 表达式 / 普通函数 , …

全链路压力测试有哪些主要作用

全链路压力测试是在软件开发和维护过程中不可或缺的一环&#xff0c;尤其在复杂系统和高并发场景下显得尤为重要。下面将详细介绍全链路压力测试的主要作用。 一、全链路压力测试概述 全链路压力测试是指对软件系统的全部组件(包括前端、后端、数据库、网络、中间件等)在高负载…

vue/vue3/js来动态修改我们的界面浏览器上面的文字和图标

前言&#xff1a; 整理vue/vue3项目中修改界面浏览器上面的文字和图标的方法。 效果&#xff1a; vue2/vue3: 默认修改 public/index.html index.html <!DOCTYPE html> <html lang"en"><head><link rel"icon" type"image/sv…

HarmonyOS自定义组件生命周期函数介绍

aboutToAppear 在创建自定义组件的新实例后&#xff0c;在执行其build()函数之前执行。允许在aboutToAppear函数中改变状态变量&#xff0c;更改将在后续执行build()函数中生效。 aboutToDisappear 在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变…

Redis:原理速成+项目实战——Redis实战10(Redis消息队列实现异步秒杀)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;Redis&#xff1a;原理速成项目实战——Redis实战9&#xff08;秒杀优化&#xff09; &#x1f4da;订阅专栏&#xff1a;Redis&…

wxWidgets实战:使用mpWindow绘制阻抗曲线

选择模型时&#xff0c;需要查看model的谐振频率&#xff0c;因此需要根据s2p文件绘制一张阻抗曲线。 如下图所示&#xff1a; mpWindow 左侧使用mpWindow&#xff0c;右侧使用什么&#xff1f; wxFreeChart https://forums.wxwidgets.org/viewtopic.php?t44928 https://…

实战环境搭建-linux下安装tomcat

安装tomcat Index of /dist/tomcat/tomcat-9/v9.0.8/bin 下载apache-tomcat-9.0.8.tar.gz&#xff0c;可以使用wget; 2、将压缩包tar -zxvf apache-tomcat-9.0.8.tar.gz解压到/home/tomcat 3、修改环境变量 vi /etc/profile export JAVA_HOME/home/java/jdk1.8.0_221 expo…

IntelliJ IDEA Java 连接 mysql 配置(附完整 demo)

下载 MySQL 驱动 从MySQL官网下载JDBC驱动的步骤如下&#xff1a; 1&#xff09;访问MySQL的官方网站&#xff1a;MySQL 2&#xff09;点击页面上方的"DOWNLOADS"菜单&#xff1b; 3&#xff09;在下载页面&#xff0c;找到"MySQL Community (GPL) Downloads…

QT第四天

头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTime>//时间类 #include<QTimerEvent>//定时器事件类 #include<QtTextToSpeech> //语言播报类 #include<QPushButton> namespace Ui { class Widget; }clas…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷⑥

2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷6 目录 需要竞赛软件包环境以及备赛资源可私信博主&#xff01;&#xff01;&#xff01; 2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷6 模块一…

前端导出Excel文件,部分数字前面0消失处理办法

详细导出可以看之前的文章 js实现导出Excel文档_js 通过 接口 导出 xlsx 代码-CSDN博客 今天的问题是导出一些数据时&#xff0c;有些字段是前面带有0的字符串&#xff0c;而导出后再excel中就被识别成了数字 如图本来字符串前面的0 都没了 解决方案 1. 导出的时候在前面加单…

微信V3支付,JSAPI 支付报错,返回 映射到值字段“子商户号/二级商户号”字符串规则校验失败

问题 最近使用 微信V3 支付的JAVA 版本&#xff0c;调用 JSAPI 支付&#xff0c;报错&#xff1a; httpResponseBody[{"code":"PARAM_ERROR","detail":{"location":null,"value":""},"message":"…

Fluids —— Whitewater (SOP)

目录 Whitewater Lifecycle Workflow Whitewater source Deformation sources Visualizing whitewater Whitewater solver Wind Foam erosion Repellants Whitewater postprocess 基于SOP的白水是对SOP FLIP工作流的增强&#xff1b;该系统与规模无关&#xff0c;无需…

day-06 构造有效字符串的最少插入数

思路 动态规划&#xff1a; Word[i]单独组成abc 如果Word[i]>word[i-1] 则word[i]和word[i-1]一起构成abc 解题方法 关系式&#xff1a;dp[i]dp[i-1]2或dp[i]dp[i-1]-1 时间复杂度&#xff1a; O(n) 空间复杂度&#xff1a; O(1) Code class Solution {/*动态规划&…

摩托车充气泵方案芯片应用设计

技术工程师在做产品方案开发之前&#xff0c;首先也是最重要的就是芯片选型。为什么这样说呢&#xff1f;那是因为芯片是整个方案设计中&#xff0c;最至关重要的一环&#xff0c;没有它&#xff0c;后面的工作将无法进行&#xff0c;只有将芯片核心基础定下来&#xff0c;后面…

zabbix监控windows主机

下载安装zabbix agent安装包 Zabbix官网下载地址: https://www.zabbix.com/cn/download_agents?version5.0LTS&release5.0.40&osWindows&os_versionAny&hardwareamd64&encryptionOpenSSL&packagingMSI&show_legacy0 这里使用zabbix agent2 安装 …

C#,迭代深化搜索(IDS)或迭代深化深度优先搜索(IDDFS)算法的源代码

摘要&#xff1a;本文介绍适合于大数据规模情况下的&#xff0c;新型的迭代深化深度优先搜索(IDDFS)算法的原理、实例及实现的C#源代码。 引言 常用的树&#xff08;或图&#xff09;遍历算法是两种&#xff1a; 广度优先搜索算法&#xff08;BFS&#xff09; 和 深度优先搜索…

LeetCode[105] 从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,15,7] …