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{
        @Override
        public 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);
                }

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

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

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

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

相关文章

导入项目运行后,报错java: Cannot find JDK ‘XX‘ for module ‘XX‘

解决方案&#xff1a; 1、删除.idea和.iml文件 2、右击此module&#xff0c;点击 Open Module Settings 在 Module SDK 中选择所安装的java版本后&#xff0c;点击右下角 Apply 3、再运行试试吧&#xff0c;成功&#xff01;

《QT实用小工具·十四》面板容器控件和图形字体示例

1、概述 源码放在文章末尾 面板容器控件包含如下功能&#xff1a; 支持所有widget子类对象&#xff0c;自动产生滚动条。 支持自动拉伸自动填充。 提供接口获取容器内的所有对象的指针。 可设置是否自动拉伸宽度高度。 可设置设备面板之间的间距和边距。 超级图形字体类…

金陵科技学院软件工程学院软件工程专业

感兴趣的小伙伴可以私信我哦~~ 是笔者写的各种高质量作业和实验哦~~ 感兴趣的小伙伴可以私信我哦~~ 是笔者写的各种高质量作业和实验哦~~ 感兴趣的小伙伴可以私信我哦~~ 是笔者写的各种高质量作业和实验哦~~ 感兴趣的小伙伴可以私信我哦~~ 是笔者写的各种高质量作业和实验哦…

【美团笔试题汇总】2023-08-26-美团春秋招笔试题-三语言题解(CPP/Python/Java)

&#x1f36d; 大家好这里是KK爱Coding &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新小米近期的春秋招笔试题汇总&#xff5e; &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f…

在线除背景抠图工具推荐,通过AI自动去除人物背景,给图片背景换色

发现了一个好的在线除水印的网站&#xff0c;这里由「易极赞」的小编来分享给大家。它就是我们今天的主角SnapEdit。 工具简介 SnapEdit 借助至极先进的人工智能技术&#xff0c;得以自动判别图像的主体与背景&#xff0c;飞速地移除背景且保留主体的细微之处和边缘轮廓&…

十六.PyEcharts常用视图(2)

目录 一.饼图 二.空心饼图(掏空) 三.玫瑰图 四.修改图例位置--全局 五.雷达图 六.时间轴 简单写一下,快速出图... 一.饼图 #饼图 import pyecharts.options as opts from pyecharts.faker import Faker from pyecharts.charts import Pie #zip() data_pie list(zip(Fa…

Floyd之蓝桥公园

Floyd Floyd算法是一种用于解决“所有点最短路径”问题的算法。这是一个动态规划算法&#xff0c;可以在任何包含向量和非负权重的图中使用。它的时间复杂度是&#xff0c;其中是图中的节点数。 首先&#xff0c;我们定义一个二维数组表示从到的最短距离&#xff0c;初始时如…

软著说明文档生成/辅助填写工具

软著说明文档生成/辅助填写工具&#xff0c;自行申请软著的话&#xff0c;软著60页源码还比较容易搞定&#xff0c;但是说明文档有格式和字数要求&#xff0c;就很烦。这个网站可以进行格式和内容的辅助填写&#xff0c;不用再把精力浪费到没用的调整格式上&#xff0c;网站地址…

揭秘SCQL:隐私计算的未来之路

1.SCQL使用/集成最佳实践 隐语隐私计算中SCQL&#xff08;Secure Collaborative Query Language&#xff09;的设计旨在提供一种便捷且安全的方式来处理多方参与下的隐私敏感数据查询与分析&#xff0c;而无需暴露原始数据给任何一方。以下是基于以上所记录信息的SCQL使用和集…

Jackson @JsonUnwrapped注解扁平化 序列化反序列化数据

参考资料 Jackson 2.x 系列【7】注解大全篇三JsonUnwrapped 以扁平的数据结构序列化/反序列化属性Jackson扁平化处理对象 目录 一. 前期准备1.1 前端1.2 实体类1.3 Controller层 二. 扁平化序列反序列化数据2.1 序列化数据2.2 反序列化数据 三. 前缀后缀处理属性同名四. Map数…

Pillow教程10:设计博文的文字背景封面图,再也不担心找不到不素材了

---------------Pillow教程集合--------------- Python项目18&#xff1a;使用Pillow模块&#xff0c;随机生成4位数的图片验证码 Python教程93&#xff1a;初识Pillow模块&#xff08;创建Image对象查看属性图片的保存与缩放&#xff09; Pillow教程02&#xff1a;图片的裁…

C#中ref和out相关知识点

知识点一&#xff1a; 知识点二&#xff1a; 知识点三&#xff1a; 测试&#xff1a; 总结&#xff1a; 练习

逐步学习Go-WaitGroup【连字都懒得写了,直接Show my Code】

package waitgroup_testimport ("fmt""runtime""sync""testing""time""github.com/stretchr/testify/assert" )// 这是对Go语标准库中sync包下的WaitGroup的描述。// WaitGroup用于等待一组并发的goroutine结结束…

理解VAE,可视化

引言 本文主要摘抄自&#xff1a;Understanding Variational Autoencoders (VAEs), Joseph Rocca, Sep 24, 2019&#xff0c;同时会加一些自己的理解和对原文的解释。 关于数据生成&#xff0c;目前深度生成模型中主流的有&#xff1a; 生成对抗网络——GANs&#xff0c;这是…

【Python的第三方库】flask

1. Flask是什么&#xff1f; 基于python的web后端开发轻量级框架&#xff1b; 基于MVT设计模式即Models,Views,Templates(html模板语言) 2.中文文档&#xff1a; https://dormousehole.readthedocs.io/en/2.1.2/index.html 3.依赖3个库&#xff1a; Jinja2 模版&#xff1…

armlinux-外部中断

s3c2440的中断框图 如果我们单纯配置一个按键的外部中断&#xff0c;就不存在子中断与优先级的问题。 由于是按键的外部中断&#xff0c;通过引脚的高低电平来触发。所以我们要先配置引脚的功能。 我们使用按键1&#xff0c;终端源为EINT8&#xff0c;对应引脚GPG0 通过用户手…

物联网实战--入门篇之(八)嵌入式-空气净化器

目录 一、风扇调速 二、通讯协议 三、净化器运行逻辑 一、风扇调速 单片机是不能直接驱动电机的&#xff0c;因为主芯片的驱动电流比较小(50mA左右)&#xff0c;他们之间正常还要有个电机驱动器&#xff0c;常用的有TB6612、L298和L9110等&#xff0c;目前项目用的这个电机它…

全国航空机场分布矢量数据/旅游景点poi/全国港口码头分布/地铁站分布/火车站分布/POI矢量数据

民用航空机场是指针对包括跑道型机场、表面直升机场、高架直升机场、船上直升机场、直升机水上平台、滑翔机场、水上机场、有人操纵气球施放场以及其他专供民用航空器起降的划定区域。民用航空机场分为通用航空机场和公共运输机场&#xff1b;不包括临时机场和专用机场。 根据中…

谷歌修复了安卓中的 28 个漏洞和 Pixel 设备中的 25 个错误

关注公众号&#xff1a; 网络研究观 获取更多信息 本周&#xff0c;谷歌工程师修复了Android 中的 28 个漏洞和 Pixel 设备中的 25 个错误&#xff0c;其中包括两个已经被利用的问题。 据报道&#xff0c;网络取证已利用 Google Pixel 0day 漏洞在没有 PIN 码的情况下解锁智能…

【附下载】2024全行业数字化转型企业建设解决方案PPT合集

精品推荐&#xff0c;2024全行业数字化转型企业建设解决方案PPT合集&#xff0c;精品PPT源格式共21份。 以下是资料目录&#xff0c;如需下载&#xff0c;请前往星球获取&#xff1a; 1.制造业数字化转型解决方案及应用.pptx 2.医院数字化网络解决方案.pptx 3.食品饮料工厂数字…