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

news2024/12/23 7:32:47

本篇博客将进行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);
        }
    }
}

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

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

相关文章

【深入理解】width 的默认值,2024年最新面试复盘

先自我介绍一下&#xff0c;小编浙江大学毕业&#xff0c;去过华为、字节跳动等大厂&#xff0c;目前阿里P7 深知大多数程序员&#xff0c;想要提升技能&#xff0c;往往是自己摸索成长&#xff0c;但自己不成体系的自学效果低效又漫长&#xff0c;而且极易碰到天花板技术停滞…

“华为杯“华南理工大学程序设计竞赛 L-再一道好题

题目 #include<bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second const int maxn 1e6 5; const int inf 1e9 5;using namespace std;int n, m;void solve(){int res 0;int q;string s;int k;cin …

【软考】UML中的图之类图

目录 1. 说明2. 图示3. 类图使用方式3.1 对系统的词汇建模3.2 对简单的协作建模3.3 对逻辑数据库模式建模 1. 说明 1.类图&#xff08;Class Diagram&#xff09;展现了一组对象、接口、协作和它们之间的关系。2.在面向对象系统的建模中所建立的最常见的图是类图。3.类图给出系…

【浪漫 罗盘时钟 Js、css实现(附源代码) 美化版本】,前端面试必问的HashMap

先自我介绍一下&#xff0c;小编浙江大学毕业&#xff0c;去过华为、字节跳动等大厂&#xff0c;目前阿里P7 深知大多数程序员&#xff0c;想要提升技能&#xff0c;往往是自己摸索成长&#xff0c;但自己不成体系的自学效果低效又漫长&#xff0c;而且极易碰到天花板技术停滞…

LeetCode 11.盛最多谁的容器

目录 题目描述 方法一 双指针 思路&#xff1a; 代码&#xff1a; 题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的…

Python学习笔记(37)——用xlwings库生成excel

老规矩先pip入xlwings库 STEP1:下载xlwings库 windowsr>>cmd>>pip install xlwings (如果需要不同版本可以到pypi上搜&#xff09; STEP2:完成EXCEL初级创建 请打开您的编写软件~~~~~&#xff08;小编的显示结果为PYCHARM编写的&#xff0c;因为颜色标注好看(…

最优算法100例之49-链表环-计算环的长度

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 定义两个指针,一个指针走两步,一个指针走一步,第一次相遇时开始计数,第二次相遇时停止计数,最后计数器的值就是环的长度…

用 LLaMA-Factory 在魔搭微调千问

今天在魔搭上把千问调优跑通了&#xff0c;训练模型现在在 Mac 还不支持&#xff0c;需要用 N 卡才可以&#xff0c;只能弄个N 卡的机器&#xff0c;或者买个云服务器。魔搭可以用几十个小时&#xff0c;但是不太稳定&#xff0c;有的时候会自动停止。 注册账号 直接手机号注…

康耐视visionpro-CoglntersectLineLineTool操作说明工具详细说明

◆CogIntersectLineLineTool功能说明&#xff1a; 创建两条线的交点 备注&#xff1a;在“Geometry-Intersection”选项中的所有工具都是创建两个图形的交点工具&#xff0c;其中包括圆与圆的交点、线与圆的交点、线与线的交点、线与圆的交点等&#xff0c;工具使用的方法相似。…

蚂蚁云科技集团应用研究院院长李亚锋先生受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 蚂蚁云科技集团应用研究院院长李亚锋先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“探索AI技术对项目管理发展的影响”。大会将于5月25-26日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; 19…

DAY13|239. 滑动窗口最大值、347.前K个高频元素

239. 滑动窗口最大值、347.前 K 个高频元素 239. 滑动窗口最大值347.前 K 个高频元素 239. 滑动窗口最大值 难度有些大啊… 其实队列没有必要维护窗口里的所有元素&#xff0c;只需要维护有可能成为窗口里最大值的元素就可以了&#xff0c;同时保证队列里的元素数值是由大到小…

PCIe错误定义与分类

前言&#xff1a; PCI总线中定义两个边带信号&#xff08;PERR#和SERR#&#xff09;来处理总线错误。其中PERR#主要对应的是普通数据奇偶校检错误&#xff08;Parity Error&#xff09;&#xff0c;而SERR#主要对应的是系统错误&#xff08;System Error&#xff09;。具体如下…

计算机网络:数据链路层 - CSMA/CA协议

计算机网络&#xff1a;数据链路层 - CSMA/CA协议 CSMA/CA概述帧间间隔工作原理退避算法虚拟载波监听 CSMA/CA概述 讲解CSMA/CA之前&#xff0c;我们回顾一下CSMA/CD的三个特性&#xff1a; 多址接入MA&#xff1a;多个主机连接在一条总线上&#xff0c;竞争使用总线 载波监听…

把握零碎时间,开启长期副业兼职之旅!在家也能轻松赚钱!

转眼间&#xff0c;2024年已悄然走过三分之一。这一年&#xff0c;外界环境似乎并不那么友好&#xff0c;但对我而言&#xff0c;我的月收入仍然相对稳定。我找到的副业让我每月能赚到3000元以上&#xff0c;这让我深感庆幸。 现实中&#xff0c;只依赖主业工资的日子确实艰辛…

解一个结构间的方程组

在行和列可自由变换的平面上3点结构有6个 4点结构有16个 现在计算4a13a1&#xff0c;得到结果为 3--5*1--13*4--15*2--19*2--23*2--25*1--35*2--41*2--66*1--69*2--75*2--77*2--85*2--98*1--111*2--118*4--119*6--123*2--190* 有19个7点结构符合要求&#xff0c;其中7a123有6种…

机器学习算法快速入门

文章目录 0.简介1.常用术语1) 模型2) 数据集3) 样本&特征4) 向量5) 矩阵6)假设函数&损失函数7&#xff09;拟合&过拟合&欠拟合 2.线性回归3.梯度下降求极值4.Logistic回归算法&#xff08;分类问题&#xff09;5.KNN最邻近分类算法6.朴素贝叶斯分类算法7.决策树…

Openwrt21.02支持SKW78(MT7621)

1.获取SDK 1.下载Openwrt源码 下载链接&#xff1a; git clone --branch openwrt-21.02 https://gitee.com/cocos_yang/openwrt.git 下载完后&#xff0c;会有一个openwrt目录&#xff0c;进入openwrt目录 cd openwrt 修改feeds.conf.default的内容&#xff0c;如下所示&#x…

Unity笔记之下拉刷新列表

这样的效果&#xff1b; 代码&#xff1a; using System; using System.Collections; using System.Collections.Generic; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI;public class ScrollRectUpdateView : Mon…

Three.js--》穿越虚拟门槛打造的3D登录界面

今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目中才能灵活将所学知识运用起来&#xff0c;话不多说直接开始。 目录 项目搭建 初始化three代码 添加背景与地球 星星动画效果 星云动画效果 实现登录框效果 项目搭建…

C语言——内存函数的实现和模拟实现

1. memcpy 使用和模拟实现 void * memcpy ( void * destination, const void * source, size_t num ); 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。 这个函数在遇到 \0 的时候并不会停下来。 如果source和destination有任何的重叠&am…