NIO群聊系统的实现

一、前言

通过NIO编写简单版聊天室,客户端通过控制台输入发送消息到其他客户端。注意:并未处理粘包半包问题。

二、逻辑简述

服务器:

1)创建服务器NIO通道,绑定端口并启动服务器
2)开启非阻塞模式
3)创建选择器、并把通道注册到选择器上,关心的事件为新连接
4)循环监听选择器的事件,
5)监听到新连接事件:5.1) 建立连接、创建客户端通道5.2)客户端通道设置非阻塞5.3)客户端注册到选择器上,关心的事件为读
6)监听到读 事件6.1)获取到发送数据的客户端通道6.2)把通道数据写入到一个缓冲区中6.3)打印数据6.4)发送给其他注册在选择器上的客户端,排除自己

客户器:

1)创建客户端通道,连接服务器 ip和端口
2)创建选择器,注册客户端通道到选择器上,关心的事件为读
3)开启一个线程 循环监听选择器事件
4)监听到读事件后4.1)从通道中把数据读到缓冲区中4.2)打印数据
5)主线程循环用scanner 来监听控制台输入5.1)有输入后 发送给服务器

三、代码

服务器:

 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;/***/
public class GroupChatServer {private int port = 8888;private ServerSocketChannel serverSocketChannel;private Selector selector;public GroupChatServer() throws IOException {serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.bind(new InetSocketAddress(port));//创建选择器selector = Selector.open();//通道注册到选择器上,关心的事件为 OP_ACCEPT:新连接serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("server is ok");}public void listener() throws IOException {for (; ; ) {if (selector.select() == 0) {continue;}//监听到时间Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()) {SelectionKey selectionKey = iterator.next();if (selectionKey.isAcceptable()) {//新连接事件newConnection();}if (selectionKey.isReadable()) {//客户端消息事件clientMsg(selectionKey);}iterator.remove();}}}/*** 客户端消息处理*/private void clientMsg(SelectionKey selectionKey) throws IOException {SocketChannel socketChannel = (SocketChannel) selectionKey.channel();ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();try {//通道数据读取到 byteBuffer缓冲区socketChannel.read(byteBuffer);//创建一个数组用于接受 缓冲区的本次写入的数据。byte[] bytes = new byte[byteBuffer.limit()];//转换模式 写->读byteBuffer.flip();//获取数据到 bytes 中 从位置0开始到limit结束byteBuffer.get(bytes, 0, byteBuffer.limit());String msg = socketChannel.getRemoteAddress() + "说:" + new String(bytes, "utf-8");//倒带这个缓冲区。位置设置为零,标记为-1.这样下次写入数据会从0开始写。但是如果下次的数据比这次少。那么使用 byteBuffer.array方法返回的byte数组数据会包含上一次的部分数据//例如 上次写入了 11111 倒带后 下次写入了 22 读取出来 却是 22111byteBuffer.rewind();System.out.println(msg);//发送给其他客户端sendOuterClient(msg, socketChannel);} catch (Exception e) {System.out.println(socketChannel.getRemoteAddress() + ":下线了");socketChannel.close();}}/*** 发送给其他客户端** @param msg           要发送的消息* @param socketChannel 要排除的客户端* @throws IOException*/private void sendOuterClient(String msg, SocketChannel socketChannel) throws IOException {//获取selector上注册的全部通道集合Set<SelectionKey> keys = selector.keys();for (SelectionKey key : keys) {SelectableChannel channel = key.channel();//判断通道是客户端通道(因为服务器的通道也注册在该选择器上),并且排除发送人的通道if (channel instanceof SocketChannel && !channel.equals(socketChannel)) {try {((SocketChannel) channel).write(ByteBuffer.wrap(msg.getBytes()));} catch (Exception e) {channel.close();System.out.println(((SocketChannel) channel).getRemoteAddress() + ":已下线");}}}}/*** 新连接处理方法* @throws IOException*/private void newConnection() throws IOException {//连接获取SocketChannelSocketChannel socketChannel = serverSocketChannel.accept();//设置非阻塞socketChannel.configureBlocking(false);//注册到选择器上,关心的事件是读,并附带一个ByteBuffer对象socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));System.out.println(socketChannel.getRemoteAddress() + " 上线了");}public static void main(String[] args) throws IOException {GroupChatServer groupChatServer = new GroupChatServer();//启动监听groupChatServer.listener();}
}

客户端:

 
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.Scanner;
import java.util.Set;/**    */
public class GroupChatClient {private Selector selector;private SocketChannel socketChannel;public GroupChatClient(String host, int port) throws IOException {socketChannel = SocketChannel.open(new InetSocketAddress(host, port));socketChannel.configureBlocking(false);selector = Selector.open();//注册事件,关心读事件socketChannel.register(selector, SelectionKey.OP_READ);System.out.println("我是:" + socketChannel.getLocalAddress());}/*** 读消息*/private void read() {try {if(selector.select() == 0){//没有事件,returnreturn;}Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()){SelectionKey selectionKey = iterator.next();if(selectionKey.isReadable()){//判断是 读 事件SocketChannel socketChannel = (SocketChannel)selectionKey.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//读取数据到  byteBuffer 缓冲区socketChannel.read(byteBuffer);//打印数据System.out.println(new String(byteBuffer.array()));}iterator.remove();}} catch (IOException e) {e.printStackTrace();}}/*** 发送数据* @param msg 消息* @throws IOException*/private void send(String msg) throws IOException {socketChannel.write(ByteBuffer.wrap(new String(msg.getBytes(),"utf-8").getBytes()));}public static void main(String[] args) throws IOException {//创建客户端 指定 ip端口GroupChatClient groupChatClient = new GroupChatClient("127.0.0.1",8888);//启动一个线程来读取数据new Thread(()->{while (true){groupChatClient.read();}}).start();//Scanner 发送数据Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()){String s = scanner.nextLine();//发送数据groupChatClient.send(s);}}
}

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

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

相关文章

C++ //练习 10.24 给定一个string,使用bind和check_size在一个int的vector中查找第一个大于string长度的值。

C Primer&#xff08;第5版&#xff09; 练习 10.24 练习 10.24 给定一个string&#xff0c;使用bind和check_size在一个int的vector中查找第一个大于string长度的值。。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /*****…

Altium Designer 22焊接辅助工具 Interactivehtmlbom 插件使用教程

目录 AD22 由Interactivehtmlbom 插件生成的焊接辅助图&#xff0c;交互方式很多&#xff0c;十分方便的为我们提供便利。 介绍安装教程&#xff1a; 前去这个网站 Interactivehtmlbom 插件&#xff0c;单击下载zip 下载完成后找个地方解压&#xff0c;双击Initablelize.bat文…

河道水库雨水情自动测报系统

随着科学技术的不断进步&#xff0c;以及城市化进程的影响&#xff0c;水库的管理变得更加复杂&#xff0c;要实现城市水库的精细化管理&#xff0c;必须借助先进的信息技术手段为支撑&#xff0c;实现对三防、水资源、供水安全的实时监控&#xff0c;优化管理模式和创新管理手…

餐饮废水处理设备定制厂家

诸城市鑫淼环保小编带大家了解一下餐饮废水处理设备定制厂家 1.餐饮废水问题 餐饮业是重要的经济领域&#xff0c;但其废水排放带来的环境问题不容忽视。餐饮废水含有高浓度的有机物、油脂、残渣等&#xff0c;若未经处理直接排放&#xff0c;会严重污染水源&#xff0c;危害环…

Matlab 机器人工具箱 例程:运动学+动力学+路径规划+可视化

文章目录 1 创建机器人2 机器人显示3 机器人示教4 机器人路径规划&#xff1a;给定关节角路径5 机器人路径规划&#xff1a;给定末端位姿&#xff0c;求关节角路径6 工作空间可视化参考链接 1 创建机器人 clc;clear;close all; deg pi/180;L1 Revolute(d, 0, a, 0, alpha, 0,…

【Python】快速入门Python一天学完基础语法

文章目录 前言1. HelloWorld2. 变量与数据类型2.1 变量2.2 数据类型2.2.1 String 字符串类型2.2.2 基本类型转换2.2.2 元组2.2.3 字典2.2.4 拆包 2.3 运算2.3.1 双除号/双乘号2.3.2 常见运算函数举例2.3.3 布尔运算 3. 控制流程3.1 if-else 语句3.2 while 循环3.3 for 循环 4. …

子线程如何获取Request

子线程获取Request 有时候在进行业务处理时对于一些对于业务不那么重要且对于返回结果无关的情况会开启一个新的线程进行处理&#xff0c;但是在开启新线程进行处理时发现无法从RequestContextHolder中获取到当前的请求&#xff0c;取出来是null 这是因为RequestContextHolder中…

逆变器专题(14)-弱电网下的LCL逆变器控制以及谐振峰问题(1)

相应仿真原件请移步资源下载 LCL滤波器 LCL滤波器因其本身为一个二阶系统&#xff0c;其本身就会引发谐振&#xff0c;导致相应谐振频率处的增益得到放大&#xff0c;进而产生谐波等问题&#xff1b;另一方面&#xff0c;在弱电网下&#xff0c;逆变器会与电网阻抗发生耦合&am…

ARM总结and复习

安装交叉编译工具链 a. 为什么安装 因为arm公司的指令集在不断迭代升级&#xff0c;指令集日益增多,而架构是基于指令集研发的&#xff0c;所以架构不一样&#xff0c;指令集也不一样 eg:arm架构使用的是arm指令集 x86架构使用的是x86指令集 而我们日常开发环境中linux的架构…

重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

大家好我是苏麟 , 今天开始又一个专栏开始了(又一个坑 哈哈) . 重载&#xff08;Overload&#xff09;和重写&#xff08;Override&#xff09;的区别。重载的方法能否根据返回类型进行区分&#xff1f; 方法的重载和重写都是实现多态的方式&#xff0c;区别在于前者实现的是编…

Java进阶-测试方法

来学习一下软件测试相关的方法&#xff0c;了解一下黑盒测试和白盒测试&#xff0c;以及后面要用到的JUnit单元测试。JUnit单元测试也属于白盒测试&#xff0c;这次内容较少且相对简单。 一、软件测试方法 1、黑盒测试 不需要写代码&#xff0c;给输入值&#xff0c;看程序…

分付在哪些商户可以使用消费,微信分付怎么提取出来到余额上面来?

分付是一款信用支付产品&#xff0c;用户可以使用分付进行线上线下的消费支付。下面是使用分付的一些方法&#xff1a; - 开通分付&#xff1a;在微信中搜索并开通分付服务&#xff0c;按照提示完成实名认证和绑定银行卡等操作。 - 线上支付&#xff1a;在支持分付的线上商户…

【Web - 框架 - Vue】随笔 - 通过CDN的方式使用VUE 2.0和Element UI

通过CDN的方式使用VUE 2.0和Element UI - 快速上手 VUE 网址 https://cdn.bootcdn.net/ajax/libs/vue/2.7.16/vue.js源码 https://download.csdn.net/download/HIGK_365/88815507测试 代码 <!DOCTYPE html> <html lang"en"> <head><meta …

『大模型笔记』最大化大语言模型(LLM)的性能(来自OpenAI DevDay 会议)

最大化大语言模型(LLM)的性能(来自OpenAI DevDay 会议) 文章目录 一. 内容介绍1.1. 优化的两个方向(上下文优化和LLM优化)1.2. 提示工程:从哪里开始1.3. 检索增强生成:拓展知识边界1.4. 微调:专属定制二. 参考文献一. 内容介绍 简述如何以可扩展的方式把大语言模型(LLMs)…

剑指offer》15--二进制中1的个数[C++]

1. 题目描述 输入一个整数&#xff0c;输出该数二进制表示中 1 的个数。 2. 解题思路 如果对负数直接右移&#xff0c;会导致最高位一直补1&#xff0c;最终变成0xFFFF死循环。 常规做法&#xff1a; 3. 代码实现 #include<iostream> #include<vector> using…

Leetcoder Day38| 动态规划part05 背包问题

1049.最后一块石头的重量II 有一堆石头&#xff0c;每块石头的重量都是正整数。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果如下&#xff1a; 如果 x y&#xff0c;那…

012集—二维轻量多线段LWpolyline设置凸度bulge——vba实现

本文主要讲LightweightPolyline ,即轻量多段线。 ObjectARX 中提供了三种多段线的相关类:AcDbPolyline&#xff08;对应vba中lightweightpolyline&#xff09; 、AcDb2dPolyline (对应vba中polyline)和 AcDb3dPolyline Polyline就是&#xff08;轻量&#xff09;多段线&…

蓝牙BLE 5.0、5.1、5.2和5.3区别

随着科技的不断发展&#xff0c;蓝牙技术也在不断进步&#xff0c;其中蓝牙BLE&#xff08;Bluetooth Low Energy&#xff09;是目前应用广泛的一种蓝牙技术&#xff0c;而BLE 5.0、5.1、5.2和5.3则是其不断升级的版本。本文将对这四个版本的区别进行详细的比较。 一、BLE 5.0…

未来趋势:个人化资源整合将成为主流

随着科技的发展和社会的进步&#xff0c;我们正步入一个高度个性化和数字化的时代&#xff0c;在这个时代中&#xff0c;资源的整合与分配模式正发生着深刻的变革。本文试图论证&#xff0c;未来的资源整合将更倾向于个人化&#xff0c;即资源将以更加灵活、定制化的方式流向个…

【mysql技巧】如何在这个mysql语句执行前加个前提,也就是只有表里没有相同数据才进行添加插入操作

文章目录 我们正常的mysql插入数据语句加个前提完结 我们正常的mysql插入数据语句 INSERT INTO guild_nakadai.admin_role_permission (role_id, permission_id, type) VALUES ((SELECT id FROM guild_nakadai.admin_roles WHERE name"员工"),(SELECT id FROM guil…