Java网络编程(二)NIO实现简易的多人聊天

news2024/11/26 1:28:14

服务端实现

package com.bierce.io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
//服务器端
public class NIOChatServer {
public static void main(String[] args) {
	try {
		new NIOChatServer().startServer(); //启动服务器
	} catch (IOException e) {
		throw new RuntimeException(e);
	}
}
//服务端启动方法
public void startServer() throws IOException {
	//1. 创建Selector选择器
	Selector selector = Selector.open();
	//2.创建ServerSocketChannel通道
	ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
	//3.channel通道绑定监听端口,并设置为非阻塞模式
	serverSocketChannel.bind(new InetSocketAddress(8000));
	serverSocketChannel.configureBlocking(false);
	//4.channel注册到selector上,设置为就绪接收状态
	serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
	System.out.println("Server starts Successfully!");
	//5.自旋,实时监听客户端状态
	for (;;) {
		//获取所有就绪的Channel
		int readChannels = selector.select();
		if (readChannels == 0){ //没有接入的客户端
			continue;
		}
		Set<SelectionKey> selectionKeys = selector.selectedKeys();
		//遍历可用的Channel
		Iterator<SelectionKey> iterator = selectionKeys.iterator();
		while (iterator.hasNext()){
			SelectionKey selectionKey = iterator.next();
			//6.根据就绪状态,调用对应方法实现具体业务操作
			//6.1 accept状态
			if (selectionKey.isAcceptable()){
				acceptOperator(serverSocketChannel,selector);
			}
			//6.2 readable状态
			if (selectionKey.isReadable()){
				readOperator(selector,selectionKey);
			}
			iterator.remove(); //获取成功后没有必要保留需要移除
		}
	}
}
//处理可读状态的方法实现
private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
	//从SelectionKey获取到已经就绪的通道
	SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
	ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
	//自旋读取客户端信息
	int readLength = socketChannel.read(byteBuffer);
	String message = "";
	if (readLength > 0){
		byteBuffer.flip(); //切换读模式
		message += Charset.forName("UTF-8").decode(byteBuffer);
	}
	//注册channel到selector,监听可读状态
	socketChannel.register(selector, SelectionKey.OP_READ);
	if (message.length() > 0){
		//把客户端发送过来的信息广播到其他客户端
		System.out.println(message);
		castOtherClients(message,selector,socketChannel);
	}
}
//把客户端发送的消息广播给其他客户端
private void castOtherClients(String message, Selector selector, SocketChannel socketChannel) throws IOException {
	//获取所有已经接入的Channel
	Set<SelectionKey> selectionKeys = selector.keys();
	//对除自己以外的channel进行广播
	for (SelectionKey selectionKey:selectionKeys) {
		//获取每个Channel
	   Channel targetChannel = selectionKey.channel();
	   if (targetChannel instanceof SocketChannel && targetChannel != socketChannel){ //排除服务器以及发送方客户端自己
		   ((SocketChannel) targetChannel).write(Charset.forName("UTF-8").encode(message));
	   }
	}
}
//处理可接收状态的方法实现
private void acceptOperator(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
	//接入状态:创建socketChannel
	SocketChannel socketChannel = serverSocketChannel.accept();
	//socketChannel设置为非阻塞并注册到selector上
	socketChannel.configureBlocking(false);
	socketChannel.register(selector,SelectionKey.OP_READ);
	//回复给客户端信息
	socketChannel.write(Charset.forName("UTF-8")
			.encode("Welcome to MyChatRoom, Please notice your Info!")); //UTF-8编码
}
}

客户端实现

package com.bierce.io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;
//客户端实现
public class NIOChatClient {
public void startClient(String name) throws IOException {
	//客户端连接服务器端
	SocketChannel socketChannel =
			SocketChannel.open(new InetSocketAddress("127.0.0.1",8000));
	//接收服务器端响应数据
	Selector selector = Selector.open();
	socketChannel.configureBlocking(false);
	socketChannel.register(selector, SelectionKey.OP_READ);
	//创建客户端线程
	new Thread(new ClientThread(selector)).start();
	//模拟向服务器发送消息
	Scanner sc = new Scanner(System.in);
	while (sc.hasNextLine()){
		String msg = sc.nextLine();
		if (msg.length() >0){
			socketChannel.write(Charset.forName("UTF-8").encode(name + ":" + msg));
		}
	}
}
}
class ClientThread implements Runnable{
    private Selector selector;
    public ClientThread(Selector selector) {
        this.selector = selector;
    }
    @Override
    public void run() {
        try {
            for (;;) {
                //获取所有就绪的Channel
                int readChannels = selector.select();
                if (readChannels == 0){ //没有接入的客户端
                    continue;
                }
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                //遍历可用的Channel
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    iterator.remove(); //获取成功后没有必要保留需要移除
                    //readable状态
                    if (selectionKey.isReadable()){
                        readOperator(selector,selectionKey);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //处理可读状态的方法实现
    private void readOperator(Selector selector, SelectionKey selectionKey) throws IOException {
        //从SelectionKey获取到已经就绪的通道
        SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        //自旋读取客户端信息
        int readLength = socketChannel.read(byteBuffer);
        String message = "";
        if (readLength > 0){
            byteBuffer.flip(); //切换读模式
            message += Charset.forName("UTF-8").decode(byteBuffer);
        }
        //注册channel到selector,监听可读状态
        socketChannel.register(selector, SelectionKey.OP_READ);
        if (message.length() > 0){
            System.out.println(message); //该客户端控制台输出服务端发送过来的信息
        }
    }
}
//客户端A
package com.bierce.io;
import java.io.IOException;
public class TestAClient {
    public static void main(String[] args) {
        try {
            new NIOChatClient().startClient("A");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
//客户端B
package com.bierce.io;
import java.io.IOException;
public class TestBClient {
    public static void main(String[] args) {
        try {
            new NIOChatClient().startClient("B");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

效果图

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

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

相关文章

红绿灯识别、倒计时识别(毕业设计)

交通标志识别 本项目使用YOLO 模型&#xff0c;并在对数字信号灯进行数字识别时采用opencv算法。 环境安装 所需环境 python 3.7.11 torch1.2.00 使用 pip install -r requirements.txt安装所需的包。 文件下载 训练所需的预训练权重可在百度网盘中下载。 链接&#xf…

ARM体系结构学习笔记:任何算法可通过下面的三种模式组合而成

任何算法可通过下面的三种模式组合而成 条件跳转和无条件跳转 条件命名规则 关于比较的一些哲学问题 汇编实现if else [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R8R5cYTQ-1692236026691)(https://cdn.jsdelivr.net/gh/nzcv/picgo/202201172242…

Gin+微服务实现抖音视频上传到七牛云

文章目录 安装获取凭证Gin处理微服务处理 如果你对Gin和微服务有一定了解&#xff0c;看本文较容易。 安装 执行命令&#xff1a; go get github.com/qiniu/go-sdk/v7获取凭证 Go SDK 的所有的功能&#xff0c;都需要合法的授权。授权凭证的签算需要七牛账号下的一对有效的A…

回归预测 | MATLAB实现BES-LSSVM秃鹰搜索算法优化最小二乘支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现BES-LSSVM秃鹰搜索算法优化最小二乘支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现BES-LSSVM秃鹰搜索算法优化最小二乘支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&a…

手写模拟SpringBoot核心流程(二):实现Tomcat和Jetty的切换

实现Tomcat和Jetty的切换 前言 上一篇文章我们聊到&#xff0c;SpringBoot中内置了web服务器&#xff0c;包括Tomcat、Jetty&#xff0c;并且实现了SpringBoot启动Tomcat的流程。 那么SpringBoot怎样自动切换成Jetty服务器呢&#xff1f; 接下来我们继续学习如何实现Tomcat…

网络编程面试笔试题

一、OSI 7层模型&#xff0c;TCP/IP 4层模型 5层模型。 以及每一层的功能&#xff08;重点&#xff1a;第三层 第四层&#xff09; 答&#xff1a; 7层模型&#xff08;①物理层&#xff1a;二进制比特流传输&#xff0c;②数据链路层&#xff1a;相邻结点的可靠传输&#xf…

Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 持续更新中

Ctfshow 命令执行 web29 pregmatch是正则匹配函数&#xff0c;匹配是否包含flag&#xff0c;if(!preg_match("/flag/i", $c))&#xff0c;/i忽略大小写 可以利用system来间接执行系统命令 flag采用f*绕过&#xff0c;或者mv fl?g.php 1.txt修改文件名&#xff0c…

Docker搭建LNMP----(超详细)

目录 ​编辑 一、项目环境 1.1 所有安装包下载&#xff1a; 1.3 服务器环境 1.4任务需求 二、Ngin 2.1、建立工作目录 2.2 编写 Dockerfile 脚本 2.3准备 nginx.conf 配置文件 2.4生成镜像 2.5创建自定义网络 2.6启动镜像容器 2.7验证 nginx、 三、Mysql 3.1建立…

生产环境下的终极指南:使用 Docker 部署 Nacos 集群和 MySQL

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

WPF入门到精通:1.新建项目及项目结构

WPF&#xff08;Windows Presentation Foundation&#xff09;是一种用于创建 Windows 应用程序的技术&#xff0c;它可以通过 XAML&#xff08;Extensible Application Markup Language&#xff09;和 C# 或其他 .NET 语言来实现。WPF 提供了许多强大的 UI 控件和样式&#xf…

OpenCV实例(九)基于深度学习的运动目标检测(二)YOLOv2概述

基于深度学习的运动目标检测&#xff08;二&#xff09;YOLOv2&YOLOv3概述 1.YOLOv2概述2.YOLOv3概述2.1 新的基础网络结构&#xff1a;2.2 采用多尺度预测机制。2.3 使用简单的逻辑回归进行分类 1.YOLOv2概述 对YOLO存在的不足&#xff0c;业界又推出了YOLOv2。YOLOv2主要…

17.HPA和rancher

文章目录 HPA部署 metrics-server部署HPA Rancher部署Rancherrancher添加集群仪表盘创建 namespace仪表盘创建 Deployments仪表盘创建 service 总结 HPA HPA&#xff08;Horizontal Pod Autoscaling&#xff09;Pod 水平自动伸缩&#xff0c;Kubernetes 有一个 HPA 的资源&…

openai多模态大模型:clip详解及实战

引言 CLIP全称Constrastive Language-Image Pre-training&#xff0c;是OpenAI推出的采用对比学习的文本-图像预训练模型。CLIP惊艳之处在于架构非常简洁且效果好到难以置信&#xff0c;在zero-shot文本-图像检索&#xff0c;zero-shot图像分类&#xff0c;文本→图像生成任务…

728. 自除数 题解

题目描述&#xff1a;728. 自除数 - 力扣&#xff08;LeetCode&#xff09; 自除数 是指可以被它包含的每一位数整除的数。 例如&#xff0c;128 是一个 自除数 &#xff0c;因为 128 % 1 0&#xff0c;128 % 2 0&#xff0c;128 % 8 0。 自除数 不允许包含 0 。 给定两个整…

VCS与Verdi联仿,简要万能工程模板,持续更新中...

VCS与Verdi联仿&#xff0c;简要工程模板&#xff0c;持续更新中… 文章目录 VCS与Verdi联仿&#xff0c;简要工程模板&#xff0c;持续更新中...背景编写工程模块使用工程模板仿真结果工程下载地址 背景 学习verilog&#xff0c;故用vcs来编译verilog&#xff0c;用verdi来查…

一篇文章了解编译类成员定义

文章目录 一篇文章了解编译类成员定义 %Dictionary.CompiledClass - 编译类定义表简介索引示例表结构 %Dictionary.CompiledConstraint - 编译约束表简介索引示例表结构 %Dictionary.CompiledConstraintMethod - 编译约束表简介索引示例表结构 %Dictionary.CompiledForeignKey …

通过docker-Compose快速搭建OwnCloud网盘

目录 docker-compose文件信息 nginx文件信息 证书生成 查看文件有哪些&#xff01;&#xff01;&#xff01; 在 .yml 文件目录运行 查看容器情况并访问网页 当然&#xff0c;以下是一个使用 MySQL 5.7、Nginx 和 ownCloud 的完整 Docker Compose 示例&#xff0c;同时启用…

《Zookeeper》源码分析(十六)之 Leader是如何运行的

目录 Leader创建Leader实例lead() Leader Leader选举结束后&#xff0c;成为leader的服务器开始进行leader的工作&#xff0c;过程如下&#xff1a; 从源码中看出&#xff0c;第一步先创建Leader实例&#xff0c;第二步调用Leader.lead()方法&#xff0c;Leader的所有工作都…

【ElasticSearch】一键安装ElasticSearch与Kibana以及解决遇到的问题

目录 一、安装ES 二、安装Kibana 三、遇到的问题 一、安装ES 按顺序复制即可 docker network create es-net # 创建网络 docker pull images:7.12.1 # 拉取镜像 mkdir -p /root/es/data # 创建数据卷 mkdir -p /root/es/plugins # 创建数据卷 chmod 777 /root/es/** # 设置权…

Ubuntu20.04搭建OpenGL环境(glfw+glad)

Ubuntu20.04搭建OpenGL环境(glfwglad) Linux环境搭建 本文在VMware安装Ubuntu20.04桌面版的环境下搭建OpenGL&#xff0c;按照本文搭建完成后可以执行LearnOpenGL网站上的demo。 关于VMware可自行到VMware Workstation Pro | CN下载 关于Ubuntu20.04桌面版可自行到官网或In…