java -网络编程socket-聊天室-02

 完整版代码

java -聊天室的代码: 用于存放聊天室的项目的代码和思路导图icon-default.png?t=N7T8https://gitee.com/to-uphold-justice-for-others/java---code-for-chat-rooms.git

先引入线程的正统解释

线程(Thread)是程序执行流的最小单元。线程是操作系统分配CPU时间片的基本单位,每个线程都拥有独立的程序计数器、栈、本地方法栈等,共享进程的资源,如代码段、数据段、堆等。Java通过线程实现并发编程,提高了程序的执行效率。

 线程的常见有两种方式

继承Thread类:通过继承Thread类并重写其run()方法,可以创建线程。然后,通过调用线程的start()方法来启动线程。

public class MyThread extends Thread {  @Override  public void run() {  // 线程执行的代码  System.out.println("MyThread is running.");  }  public static void main(String[] args) {  MyThread thread = new MyThread();  thread.start(); // 启动线程  }  
}

实现Runnable接口:通过实现Runnable接口的run()方法,也可以创建线程。这种方式更加灵活,因为Java不支持多继承,但可以实现多个接口。通常,我们将线程的任务逻辑放在Runnable实现类中,然后将其传递给Thread对象来启动线程。

public class MyRunnable implements Runnable {  @Override  public void run() {  // 线程执行的代码  System.out.println("MyRunnable is running.");  }  public static void main(String[] args) {  Thread thread = new Thread(new MyRunnable());  thread.start(); // 启动线程  }  
}

如果线程要多个线程调用一个类对象的时候常用Runnable接口,单一线程调用类对象个多方法可以使用继承比较方便

这边因为是服务端去获取多个客户端的消息并进行读取操作,线程在服务端创建,即一个客户端连接到服务器时候,服务端会接受客户端的插口,并新建一个线程去处理客户端的写入服务端的数据.

这边是服务端的封装私有的内部类一个线程的类来专门处理来自客户端的数据

私有的内部类的好处,在服务的执行业务,外部类不能直接访问,保证业务安全性,因为是内部类不用向外部类中建立一个引用变量实例来访问其对应的方法和属性

引用变量

引用变量的定义通常包括变量类型(即对象类型)和变量名

// 定义一个引用变量,其类型为String  
String myString;  // 为引用变量分配一个String对象  
myString = new String("Hello, World!");  
//这里的mystring就是个引用变量

 服务端

之前在在单线程获取用户的昵称和ip地址这边需要封装成私有属性,保护用户的数据私密性

客户端需要ip是可以服务端接受的插口获取的,所以在有参构造中需要将ip赋值

全局变量相比局部变量 全局变量可以被其内部类和内部方法访问到,也容易因其遭到数据修改

局部变量可以保证在自己的类或者自己的方法中使用.

常常相比的是如果一个类中有成员变量,其非静态方法可以访问其成员变量,静态方法只能访问静态变量,静态变量是由static修饰的,常称为类的全局变量,如果其中还有内部类,类中的成员变量常称为局部实例变量,如果类中方法有变量常称为局部变量

private class ClientHandler implements Runnable{private Socket socket;private String ip;//记录当前客户端的IP地址private String nickname;//记录当前客户端的昵称public ClientHandler(Socket socket){this.socket = socket;//通过socket获取远端计算机的IP地址ip = socket.getInetAddress().getHostAddress();}public void run(){PrintWriter pw = null;try {/*Socket的方法:InputStream getInputStream()通过socket获取一个字节输入流,读取该流就可以读取到远端计算机发送过来的数据*/InputStream in = socket.getInputStream();InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);BufferedReader br = new BufferedReader(isr);nickname = br.readLine();} catch (IOException e) {//可以添加处理客户端异常断开的操作} finally {try {socket.close();} catch (IOException e) {throw new RuntimeException(e);}}}
}

同时线程也需要启动,这边使用的Runnable接口实现的所以需要新建一个线程实例化对象,

实现实例化对象.start启动线程

这要写在class Server类中

              //实例化线程任务ClientHandler handler = new ClientHandler(socket);//实例化线程Thread t = new Thread(handler);//启动线程t.start();

问题 服务端连接了多个客户端,虽然每个客户端可以给服务端发送消息,但是日常的情况,常需要客户端与客户端聊天

解决 因为客户端都是将信息传递给客户端,我们可以在服务端整合客户端的输给服务端的数据,这边的话使用集合的思想,集合是可以不固定的其长度,其内部是有初始的长度,会根据添加的数据进行扩容.这边推荐ArrayList ,是因为服务端是整合客户端数据发送,而ArrayList 相比与LinkedList查询数据快.

public class Server {
private Collection<PrintWrite> allOut =  new ArrayList<>();
}

这边需要定义在服务端一个输出流,而在客户端是输入流

服务端

这边服务端封装类一个内部类处理客户端的数据,

public class Server {private class ClientHandler implements Runnable{
public void run(){ PrintWriter pw = null;try {OutputStream out =  socket.getOutputStream();OutputStreamWriter osw = new OutputStreamWriter(out,StandardCharsets.UTF_8);BufferedWriter bw = new BufferedWriter(osw);pw = new PrintWriter(bw, true);allOut.add(pw); String message;while ((message = br.readLine()) != null) {for(PrintWriter o : allOut){o.println(nickname + "[" + ip + "]说:" + message);}}}
}

客户端

同样的这边需要封装一个私有的内部类利用线程来处理服务端发送的数据

 private class ServerHandler implements Runnable{@Overridepublic void run() {try {//通过socket获取输入流,用于读取服务端发送过来的消息InputStream in = socket.getInputStream();InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);BufferedReader br = new BufferedReader(isr);String line;while((line = br.readLine())!=null){System.out.println(line);}} catch (IOException e) {}}}

 同时线程也需要启动,这边使用的Runnable接口实现的所以需要新建一个线程实例化对象,

这边需要注意的是客户端输入昵称在客户端接受服务端数据之前

       ServerHandler handler = new ServerHandler();Thread t = new Thread(handler);t.setDaemon(true);t.start();

线程实例化对象.setDaemon(true);是将普通线程变成守护线程 

问题这边客户端接受服务端数据为什么使用守护线程

守护线程定义 

守护线程(Daemon Thread)是Java中的一种特殊线程类型,它的存在主要是为了服务其他的线程,特别是用户自定义的线程。当所有的非守护线程(即用户线程)执行完成或退出时,即使守护线程仍在运行,Java虚拟机(JVM)也会直接退出。因此,守护线程通常用于执行一些辅助工作或后台任务,例如垃圾回收、自动保存数据、心跳检测、日志记录、清理临时文件以及监控系统状态等。

守护线程的特点包括:

  1. 随主线程结束而结束:当主线程(即非守护线程)结束时,守护线程会随之被终止,不管它是否执行完毕。
  2. 不执行finally块:如果守护线程中执行的代码块中有finally块,当守护线程被终止时,finally块不会被执行。
  3. 不能持有程序运行的关键资源:由于守护线程在所有用户线程结束时可能被中断,如果持有关键资源,可能会导致数据不一致或资源泄漏。
  4. 不能用于执行必须完成的任务:由于守护线程可能随时被中断,它不适合执行必须完成的任务,例如文件写入等。

在Java中,可以通过Thread类的setDaemon(true)方法将一个线程设置为守护线程,而setDaemon(false)方法则用于取消守护线程的设置。

总之,守护线程在Java中扮演着重要的角色,用于在后台执行一些必要的辅助任务,以确保程序的正常运行和资源的有效管理。

原因

  1. 不阻塞主线程:客户端的主线程通常负责用户界面的更新、用户交互的处理等重要任务。如果接收服务端数据的操作是同步的并且需要花费较长时间,那么它可能会阻塞主线程,导致用户界面无响应或响应缓慢。通过使用守护线程来执行这项任务,主线程可以继续处理其他重要工作,从而保持用户界面的流畅性。

  2. 后台处理:接收服务端数据通常是一个后台任务,它不需要与用户进行实时交互。守护线程非常适合执行这类任务,因为它们可以在后台默默运行,而不需要用户或主线程的特别关注。

问题 客户端想要获取在线人数,以便后续的私聊(这个之后会讲) ,同时也想知道客户端下线之后人数,

解决 这边我们发现我们需要重复获取集合中的信息,而集合的长度会根据在线人数变,我们这边可以将集合信息写一个方法以减少其代码量

 public void sendMessage(String message){System.out.println(message);//先在服务端控制台上输出一下//遍历要和增删互斥,迭代器要求遍历过程中不可以通过集合方法增删元素//                for (PrintWriter o : allOut) {//发送给所有客户端for(PrintWriter o : allOut){o.println(message);}}

之前的可以简写了

集合对象.size()可以获取集合长度,正好可以统计在线人数,

这个需要在集合添加数据之后调用,业务逻辑

sendMessage(nickname+"上线了,当前在线人数:"+allOut.size());

 因为之前只考虑了将服务端整合客户端的数据集体发送,没考虑客户端下线的情况

这边如果客户端下线(包括异常断开),关闭其接口以及将其移除(昵称)

finally是保证程序一定会执行,常用与关闭文件流

finally {//处理客户端断开链接后的操作//将该客户端的输出流从共享集合allOut中删除//                    allOut.remove(pw);allOut.remove(nickname);sendMessage(nickname+"下线了,当前在线人数:"+allOut.size());//将socket关闭,释放底层资源try {socket.close();} catch (IOException e) {throw new RuntimeException(e);}

问题,线程真的安全吗,这边需要对服务端的中存储数据的集合进行增减,服务端的集合需要同时面对多个客户端输入的数据,不会引起资源抢占吗

解决这个涉及到多线程并发的问题,这个需要运用到线程锁的概念,这边暂时还在总结,我也是一个新手🤣🤣🤣

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

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

相关文章

【数据分析面试】10. 计算平均通勤时间(SQL:timestampdiff() 和datediff()区别)

题目 假设你在Uber工作。rides表包含了关于Uber用户在美国各地的行程信息。 编写一个查询&#xff0c;以获取纽约&#xff08;NY&#xff09;每位通勤者的平均通勤时间&#xff08;以分钟为单位&#xff09;&#xff0c;以及纽约所有通勤者的平均通勤时间&#xff08;以分钟为…

vue广告悬浮框,页面来回移动,鼠标放上停止,离开移动

1.dom <div class"popup-dialog" id"popupDialog" mouseover"onMmouseover" mouseout"onMouseout"><p>vue广告悬浮</p></div>2.js mounted() {this.initPopup();},beforeDestory() {if (this.times) {clearIn…

鸡尾酒排序解读

在数据处理的海洋中&#xff0c;排序算法无疑是引领我们探索数据规律的灯塔。今天&#xff0c;我们要探讨的是一种有趣且独特的排序算法——鸡尾酒排序。鸡尾酒排序&#xff0c;也被称为定向冒泡排序、双冒泡排序或搅拌排序&#xff0c;是冒泡排序的一种变体&#xff0c;它通过…

了解强化学习算法 PPO

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 介绍&#xff1a; PPO 算法&#xff0c;即 Proximal Policy Optimization&#xff08;近端策略优化&#xff09;&#xff0c;是一种强化学习算法。它的主要目的是改进策略梯度方法&#xff0c;使得训练…

flink on yarn

前言 Apache Flink&#xff0c;作为大数据处理领域的璀璨明星&#xff0c;以其独特的流处理和批处理一体化模型&#xff0c;成为众多企业和开发者的首选。它不仅能够在处理无界数据流时展现出卓越的实时性能&#xff0c;还能在有界数据批处理上达到高效稳定的效果。本文将简要…

高校心理咨询预约系统的设计与实现(论文+源码)_kaic

摘 要 随着社会的发展&#xff0c;计算机的优势和普及使得高校心理咨询预约系统的开发成为必需。高校心理咨询预约系统主要是借助计算机&#xff0c;通过对信息进行管理。减少管理员的工作&#xff0c;同时也方便广大用户对个人所需信息的及时查询以及管理&#xff0c;其次是大…

苍穹外卖——项目搭建

一、项目介绍以及环境搭建 1.苍穹外卖项目介绍 1.1项目介绍 本项目&#xff08;苍穹外卖&#xff09;是专门为餐饮企业&#xff08;餐厅、饭店&#xff09;定制的一款软件产品&#xff0c;包括 系统管理后台 和 小程序端应用 两部分。其中系统管理后台主要提供给餐饮企业内部员…

【洛谷 P8655】[蓝桥杯 2017 国 B] 发现环 题解(邻接表+并查集+路径压缩)

[蓝桥杯 2017 国 B] 发现环 题目描述 小明的实验室有 N N N 台电脑&#xff0c;编号 1 ∼ N 1 \sim N 1∼N。原本这 N N N 台电脑之间有 N − 1 N-1 N−1 条数据链接相连&#xff0c;恰好构成一个树形网络。在树形网络上&#xff0c;任意两台电脑之间有唯一的路径相连。 …

ARM架构学习笔记2-汇编

RISC是精简指令集计算机&#xff08;RISC:Reduced Instruction Set Computing&#xff09; ARM汇编概述 一开始&#xff0c;ARM公司发布两类指令集&#xff1a; ① ARM指令集&#xff0c;这是32位的&#xff0c;每条指令占据32位&#xff0c;高效&#xff0c;但是太占空间 2…

怎么让html打开网页自动跳转(多个链接)?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

#SOP#-如何使用AI辅助论文创作

#SOP#-如何使用AI辅助论文创作 ——2024.4.6 “在使用工具的时候&#xff0c;要做工具的主人” 最终交付物&#xff1a; 一份可执行的AI辅助创作论文的指导手册 交付物质量要求&#xff1a; 不为任何AI大模型付费&#xff01;不为任何降重网站付费&#xff01;通过知网检查论…

语义分割——自动驾驶鱼眼数据集

一、重要性及意义 环境感知&#xff1a;语义分割技术能够精确识别道路、车辆、行人、障碍物、交通标志和信号等各种交通场景元素。这为自动驾驶系统提供了丰富的环境信息&#xff0c;有助于车辆准确理解周围环境的结构和动态变化。决策规划&#xff1a;基于语义分割的结果&…

2024 最新版 Proteus 8.17 安装汉化教程

前言 大家好&#xff0c;我是梁国庆。 今天给大家带来的是目前 Proteus 的最新版本——Proteus 8.17。 时间&#xff1a;2024年4月4日 获取 Proteus 安装包 我已将本篇所使用的安装包打包上传至百度云&#xff0c;扫描下方二维码关注「main工作室」&#xff0c;后台回复【…

(2024,手部生成,SMPL,MANO,SD,手部参数)HanDiffuser:具有逼真手部外观的文本到图像生成

HanDiffuser: Text-to-Image Generation With Realistic Hand Appearances 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 相关工作 3. HanDiffuser 3.1. 基础 3.2. Tex…

webrtcP2P通话流程

文章目录 webrtcP2P通话流程webrtc多对多 mesh方案webrtc多对多 mcu方案webrtc多对多 sfu方案webrtc案例测试getUserMediagetUserMedia基础示例-打开摄像头getUserMedia canvas - 截图 打开共享屏幕 webrtcP2P通话流程 在这里&#xff0c;stun服务器包括stun服务和turn转发服…

docker进行jenkins接口自动化测试持续集成实战

文章目录 一、接口功能自动化测试项目源码讲解二、接口功能自动化测试运行环境配置1、下载jdk&#xff0c;maven&#xff0c;git&#xff0c;allure并配置对应的环境变量2、使用docker安装jenkins3、配置接口测试的运行时环境选择对应节点4、jenkins下载插件5、jenkins配置环境…

I2C驱动实验:验证所添加的I2C设备的设备节点

一. 简介 前面一篇文章向设备树中的 I2C1控制器节点下&#xff0c;添加了AP3216C设备节点。文章如下&#xff1a; I2C驱动实验&#xff1a;向设备树添加 I2C设备的设备节点信息-CSDN博客 本文对设备树进行测试&#xff0c;确认设备节点是否成功创建好。 二. I2C驱动实验&a…

算法刷题应用知识补充--基础算法、数据结构篇

这里写目录标题 位运算&#xff08;均是拷贝运算&#xff0c;不会影响原数据&#xff0c;这点要注意&#xff09;&、|、^位运算特性细节知识补充对于n-1的理解异或来实现数字交换找到只出现一次的数据&#xff0c;其余数据出现偶数次 >> 、<<二进制中相邻的位的…

动态多目标优化:动态约束多目标优化测试集DCP1-DCP9的TruePF(提供MATLAB代码)

一、进化动态约束多目标优化测试集DCP1-DCP9 参考文献&#xff1a; [1]G. Chen, Y. Guo, Y. Wang, J. Liang, D. Gong and S. Yang, “Evolutionary Dynamic Constrained Multiobjective Optimization: Test Suite and Algorithm,” in IEEE Transactions on Evolutionary Com…

Web3:数字化社会的下一步

随着技术的不断进步和互联网的发展&#xff0c;我们正逐渐迈入一个全新的数字化社会阶段。在这个新的时代&#xff0c;Web3作为数字化社会的重要组成部分&#xff0c;将发挥着举足轻重的作用。本文将探讨Web3在数字化社会中的意义、特点以及对未来发展的影响。 1. 重新定义数字…