【深入理解Java IO流0x0A】NIO实战-网络聊天室

本篇博客将进行NIO编程实战,实现一个简单聊天室。
我们来通过 SocketChannel 和 ServerSocketChannel 实现一个 0.1 版的聊天室,先说一下需求,比较简单,服务端启动监听客户端请求,当客户端向服务器端发送信息后,服务器端接收到后把客户端消息回显给客户端,比较呆瓜,但可以先来看一下。
image.png
来看服务端代码:

@Slf4j
public class ChatServer {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private static final int PORT = 8080;

    public static void main(String[] args) {
        new ChatServer().start();
    }

    public ChatServer(){
        try {
            // 创建一个 ServerSocketChannel,并将其绑定到指定端口
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            // 设置为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            // 创建一个Selector,并将ServerSocketChannel注册到它上面
            // 监听OP_ACCEPT事件(等待客户端连接)
            selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            log.info("聊天室服务端启动了:"+PORT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start(){
        try {
            // 无限循环,等待连接
            while(true){
                // 等待已注册的通道中有事件发生
                if(selector.select()>0){
                    Iterator<SelectionKey> iterator = selector.selectedKeys()
                            .iterator();
                    while(iterator.hasNext()){
                        // 获取到发生事件的通道的SelectionKey
                        SelectionKey key = iterator.next();
                        iterator.remove();
                        handleKey(key);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 判断SelectionKey的事件类型
    private void handleKey(SelectionKey key) throws IOException{
        // 如果是OP_ACCEPT事件,说明有新的客户端连接进来。
        // 接受新的连接,并将新连接的SocketChannel注册到 Selector上,监听OP_READ事件
        if(key.isAcceptable()){
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector,SelectionKey.OP_READ);
            log.info("客户端连接上了:"+socketChannel.getRemoteAddress());
        }else if(key.isReadable()){  // 如果是OP_READ事件,说明客户端发送了消息。读取客户端发送的消息,并将其返回给客户端
            SocketChannel socketChannel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int len = socketChannel.read(buffer);
            if(len>0){
                buffer.flip();
                String message = new String(buffer.array(),0,len);
                log.info("客户端说:"+message);
                String responseMsg = "服务端回复:"+message;
                socketChannel.write(ByteBuffer.wrap(responseMsg.getBytes()));
            }
        }
    }
}

接下来是客户端:

@Slf4j
public class ChatClient {
    private Selector selector;
    private SocketChannel socketChannel;
    private static final String HOST = "localhost";
    private static final int PORT = 8080;

    public static void main(String[] args) {
        new ChatClient().start();
    }

    public ChatClient(){
        try {
            selector = Selector.open();

            // 创建一个 SocketChannel,并连接到指定的服务器地址和端口
            socketChannel = SocketChannel.open(new InetSocketAddress(HOST,PORT));
            socketChannel.configureBlocking(false); // 设置为非阻塞模式
            // 将SocketChannel注册到selector上面,监听OP_READ事件(等待接收服务器的消息)
            socketChannel.register(selector, SelectionKey.OP_READ);
            log.info("连接到聊天室了");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void start() {
        // 启动一个新线程用于
        new Thread(()->{
            try {
                while(true){
                    // 等待已注册的通道中有事件发生
                    if(selector.select()>0){
                        // 获取到发生事件的通道的 SelectionKey
                        for(SelectionKey key:selector.selectedKeys()){
                            selector.selectedKeys().remove(key);
                            // 如果是 OP_READ 事件,说明服务器发送了消息。读取服务器发送的消息,并在控制台显示。
                            if(key.isReadable()){
                                readMessage();
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))){
            String input;
            while((input=reader.readLine())!=null){
                sendMessage(input);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    private void sendMessage(String message) throws IOException{
        if(message!=null && !message.trim().isEmpty()){
            ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
            socketChannel.write(buffer);
        }
    }

    private void readMessage() throws IOException{
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int len = socketChannel.read(buffer);
        if(len>0){
            buffer.flip();
            String message = new String(buffer.array(),0,len);
            log.info(message);
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity Shader 流光 边缘光

前言 Unity2021.3.23 一、实现原理 Time控制UV的变化,再采样一张流光贴图.即可实现流光效果。 二、效果及源码展示 1.流光效果 效果描述: 1.边缘光(菲尼尔), 2.从上到下扫描光. 效果图如下: 代码如下&#xff1a; Shader "Unlit/ScanCode" {Properties{_MainTe…

SpringBoot集成EasyExcel 3.x:高效实现Excel数据的优雅导入与导出

目录 介绍 快速开始 引入依赖 简单导出 定义实体类 自定义转换器 定义接口 测试接口 复杂导出 自定义注解 定义实体类 数据映射与平铺 自定义单元格合并策略 定义接口 测试接口 一对多导出 自定义单元格合并策略 测试数据 简单导入 定义接口 测试接口 参…

解读MongoDB官方文档获取mongo7.0版本的安装步骤与基本使用

mongo式一款NOSQL数据库&#xff0c;用于存储非结构化数据&#xff0c;mongo是一种用于存储json的数据数据&#xff0c;可以通过mongo提供的命令解析json获取想要的值。 数据模型 了解关系数据库会很熟悉database,table,row,column的概念&#xff0c;分别是数据库&#xff0c…

【SpringBoot】返回参数

返回参数 返回页面返回数据返回 html 代码返回 json 数据两数相加用户登录 返回页面 首先在 static 文件夹中创建 index.html 文件&#xff1a; 代码&#xff1a; <html> <body><h1>hello word!!!</h1><p>this is a html page</p> <…

聚类能代替分类吗?

聚类和分类是两种不同的机器学习方法&#xff0c;它们在处理数据时有着不同的目的和应用场景。 分类&#xff1a;分类是一种监督学习方法&#xff0c;它需要已标记的训练数据集。在分类中&#xff0c;算法会学习如何将输入数据映射到预定义的类别中。例如&#xff0c;给定一组包…

如何判断超级充电测试负载是否合格?

超级充电测试负载是电动汽车充电设备的重要组成部分&#xff0c;其性能直接影响到电动汽车的充电效率和安全性。因此&#xff0c;判断超级充电测试负载是否合格是非常重要的。以下是一些判断标准&#xff1a; 超级充电测试负载的充电效率是衡量其性能的重要指标&#xff0c;合格…

leetcode代码记录(Z 字形变换

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 将一个给定字符串 s 根据给定的行数 numRows &#xff0c;以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “PAYPALISHIRING” 行数为 3 时&#xff0c;排列如下&#xff1a;…

验证ElasticSearch 分词的BUG

验证ElasticSearch 分词的BUG 环境介绍 ElasticSearch 版本号: 6.7.0 BUG 重现 创建测试案例索引 PUT test_2022 {"settings": {"analysis": {"filter": {"pinyin_filter": {"type": "pinyin"}},"analy…

kafka(六)——存储策略

存储机制 kafka通过topic作为主题缓存数据&#xff0c;一个topic主题可以包括多个partition&#xff0c;每个partition是一个有序的队列&#xff0c;同一个topic的不同partiton可以分配在不同的broker&#xff08;kafka服务器&#xff09;。 关系图 partition分布图 名称为t…

互联网元搜索引擎SearXNG

最近有个很火的项目叫 FreeAskInternet&#xff0c;其工作原理是&#xff1a; 第一步、用户提出问题第二步、用 SearXNG&#xff08;本地运行&#xff09;在多个搜索引擎上进行搜索第三步、将搜索结果传入 LLM 生成答案 所有进程都在本地运行&#xff0c;适用于需要快速获取信…

【深度学习】AI修图——DragGAN原理解析

1、前言 上一篇&#xff0c;我们讲述了StyleGAN2。这一篇&#xff0c;我们就来讲一个把StyleGAN2作为基底架构的DragGAN。DragGAN的作用主要是对图片进行编辑&#xff0c;说厉害点&#xff0c;可能和AI修图差不多。这篇论文比较新&#xff0c;发表自2023年 原论文&#xff1a…

vscode中调试C++程序,解读debug步骤

下面对几个调试的按键进行解释&#xff1a; 按钮1&#xff1a;运行/继续 F5&#xff0c;真正的一步一步运行。当有断点的时候&#xff0c;只会执行断点所在行语句和开头结尾两行语句。 按钮2&#xff1a;单步跳过(又叫逐过程) F10&#xff0c;按语句单步执行。当有函数时&#…

制作适用于openstack平台的win10镜像

1. 安装准备 从MSDN下载windows 10的镜像虚拟机开启CPU虚拟化的功能。从Fedora 网站下载已签名的 VirtIO 驱动程序 ISO 。 创建15 GB 的 qcow2 镜像&#xff1a;qemu-img create -f qcow2 win10.qcow2 15G 安装必要的软件 yum install qemu-kvm qemu-img virt-manager libvir…

【Docker系列】容器访问宿主机的Mysql

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Mac M1(ARM) 使用Vmware Fusion从零搭建k8s集群

该笔记仅用于自己学习&#xff1b;上一篇安装了环境&#xff0c;这一篇开始 Mac M1(ARM) 使用Vmware Fusion从零搭建k8s集群【参考】 VMware Fusion下修改vmnet8网络和添加vmnet网络 【注意如下】 虚拟机ip修改的位置修改的&#xff0c;记得开启宿主机的mac os 网络共享&#…

有依赖的的动态规划问题

题目 题型分析 这是比较典型的动态规划的问题。动态规划是什么呢&#xff1f;本质上动态规划是对递归的优化。例如&#xff0c;兔子数列&#xff1a;f(x) f(x - 1) f(x -2), 我们知道 f 代表了计算公式&#xff0c;这里解放思想一下&#xff0c;如果 f 替换为数组&#xff0…

vue实现前端打印效果

如图效果所示&#xff08;以下演示代码&#xff09; <template><div><el-button v-print"printObj" type"primary" plain click"handle">{{ text }}</el-button><div style"display: none"><div id…

基于Springboot+Vue的Java项目-在线视频教育平台系统(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

鸿蒙OS开发指导:【应用包签名工具】

编译构建 该工具基于Maven3编译构建&#xff0c;请确认环境已安装配置Maven3环境&#xff0c;并且版本正确 mvn -version下载代码&#xff0c;命令行打开文件目录至developtools_hapsigner/hapsigntool&#xff0c;执行命令进行编译打包 mvn package编译后得到二进制文件&…

[开发日志系列]PDF图书在线系统20240415

20240414 Step1: 创建基础vueelment项目框架[耗时: 1h25min(8:45-10:10)] 检查node > 升级至最新 (考虑到时间问题,没有使用npm命令行执行,而是觉得删除重新下载最新版本) > > 配置vue3框架 ​ 取名:Online PDF Book System 遇到的报错: 第一报错: npm ERR! …