Java网络编程,多线程,IO流综合项目一一ChatBoxes
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
作者:blue
时间:2025.3.7
文章目录
- Java网络编程,多线程,IO流综合小项目一一ChatBoxes
- 1.项目介绍
- 2.项目源码剖析
- 2.1客户端源码
- 2.2客户端Sender线程Runnable源码
- 2.3客户端Receiver线程Runnable源码
- 2.4服务端源码
- 2.5服务端Runnable源码
- 3.项目心得
1.项目介绍
项目目标:实现一个C/S架构,基于TCP协议的控制台版的聊天室,带有注册,登录功能,能实现在局域网内,多个客户端,在一个聊天室中聊天
项目需求:
客户端:拥有登录,注册,聊天功能,用户名要唯一,密码第一位必须是字母,后面是纯数字,登录成功后可以直接开始聊天
服务端:对用户,登录和注册的信息进行验证,当登录成功之后,能接收客户端发来的消息,并能向所有已经登录的用户进行转发
2.项目源码剖析
2.1客户端源码
package com.bluening.Client;import java.io.*;
import java.net.Socket;
import java.util.Scanner;/** 客户端程序* 功能:1.登录* 2.注册* 3.聊天* */
public class Client {public static void main(String[] args) throws IOException, InterruptedException {//创建Socket对象,与指定服务端连接Socket socket = new Socket("127.0.0.1", 10086);//没有连接上的话,程序会报错,所以以下代码只有当连接成功才会执行System.out.println("与服务端连接成功");//主界面Scanner sc = new Scanner(System.in);//Scanner对象//字符缓冲输入流,用于接收服务端发回来的数据,利用转换流将socket的InputStream包装BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));//字符缓冲输出流,用于向服务端发送数据,利用转换流将socket的OutputStream包装BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));while (true) {System.out.println("=======控制台版聊天室=======");System.out.println("1.登录");System.out.println("2.注册");System.out.println("请输入你所需要的功能:");String choice = sc.nextLine();//输入所选择的功能if ("1".equals(choice)) {//登录模块while (true) {System.out.println("请输入用户名:");String username = sc.nextLine();System.out.println("请输入密码:");String password = sc.nextLine();bw.write("username=" + username + "&" + "password=" + password + "&" + "login");//向服务器发送信息,login表示这是一个登录请求bw.newLine();bw.flush(); // 刷新缓冲区确保数据发送//接收服务端发来的信息String line = br.readLine();if (line.equals("1")) {System.out.println("登录成功");break;} else if (line.equals("2")) {System.out.println("账户或密码错误,请重新登录");}}} else if ("2".equals(choice)) {//注册模块System.out.println("请输入用户名:");String username = sc.nextLine();String password = null;while (true) {System.out.println("请输入密码(密码只需要以字母开头,后面为纯数字):");password = sc.nextLine();//可以直接在客户端检查密码是否符合条件if (checkPassword(password)) break;else System.out.println("密码不符合条件");}bw.write("username=" + username + "&" + "password=" + password + "&" + "register");//向服务器发送信息,register表示这是一个注册请求bw.newLine();bw.flush(); // 刷新缓冲区确保数据发送//接收服务端发来的信息String line = br.readLine();if (line.equals("1")) {System.out.println("注册成功");} else if (line.equals("2")) {System.out.println("用户名重复");}continue; //注册完应该执行continue逻辑} else continue;//此处表示登录成功后开始聊天//为了使用户收发信息的操作能够同时进行,采用多线程编程来解决问题//创建一条发送信息的线程Thread sender = new Thread(new ClientSendMessageRunnable(bw));sender.start();//创建一条接收信息的线程Thread Receiver = new Thread(new ClientReceiveMessageRunnable(br));Receiver.start();//此时只让线程运行就好,可以跳出主循环break;}//释放资源//socket.close();}private static boolean checkPassword(String password) {for (int i = 0; i < password.length(); i++) {char x = password.charAt(i);if (i == 0) {if (!((x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z'))) return false;//不是以字母开头} else {if (x < '0' || x > '9') return false;//不是以纯数字作为后续}}return true;}
}
此处我对客户端源码进行大概剖析,在源代码中我的注释比较完备了,故而在此处我只挑选花费我思考时间较多的部分进行解释。
1.针对客户端其登录和注册的逻辑并不难,但这里有第一个坑点,登录与注册同样是给服务端发送username和password的信息,如何让服务端区分你是在登录还是在注册呢?
答:这里我所采用的方法是,在发送的信息字段上附带上状态信息,如果是登录那在发送的信息后面就加上"&" + “login"字段,同理如果是注册,则加上”&" + “register”,这样就方便服务端识别用户当前的行为了。
2.另外,为了保证Client与Server进行实时交互,我们在每次使用BufferedWriter bw的write方法发送信息后,我们都使用了一个flush方法,这是什么意思呢?
答:因为缓冲区是内存中的一块区域,用于临时存储数据。当进行数据写入操作时,数据不会立即被写入到目标设备(如文件、网络套接字等),而是先被存储在缓冲区中。当缓冲区满了或者满足某些条件时,才会将缓冲区中的数据一次性写入到目标设备。
flush()
方法的作用是强制将缓冲区中暂存的数据立即写入到目标设备中,无论缓冲区是否已满。具体步骤如下
检查缓冲区状态一一>写入数据到目标设备一一>清空缓冲区
3.客户端的难点,在登录成功后如何实现同时可以发送信息给服务器并可以接收服务器传来的消息?
答:我采用了多线程编程的方式来解决这个问题。针对每一个Client在其登录成功后,均有两个线程,对于Sender线程,我利用了构造方法传给了他bw,对于Receiver方法,我传给了他br。因为每个客户端是独立运行的,所以每个客户端的IO流是独立的,不会出现混乱。
对于Sender和Receiver,他们分别拥有当前Socket对象的bw和br,并不相互纠缠。
另外值得注意的是IO流的生命周期和线程的生命周期都是相互独立的。
2.2客户端Sender线程Runnable源码
package com.bluening.Client;import java.io.BufferedWriter;
import java.util.Scanner;//为了使用户收发信息的操作能够同时进行,采用多线程编程来解决问题
public class ClientSendMessageRunnable implements Runnable{//利用构造方法来传递,针对Socket的输出流对象//输入对象BufferedWriter bw;public ClientSendMessageRunnable(BufferedWriter bw) {this.bw = bw;}@Overridepublic void run() {Scanner sc = new Scanner(System.in);try {while(true){System.out.println("请输入你要发送的信息:");if (sc.hasNextLine()) {String message = sc.nextLine();// 检查输入是否为空,如果为空则继续等待有效输入if (!message.isEmpty()) {bw.write(message);bw.newLine();bw.flush();}}}} catch (Exception e) {throw new RuntimeException(e);}}
}
2.3客户端Receiver线程Runnable源码
package com.bluening.Client;import java.io.BufferedReader;
import java.io.IOException;public class ClientReceiveMessageRunnable implements Runnable{BufferedReader br;public ClientReceiveMessageRunnable(BufferedReader br) {this.br = br;}@Overridepublic void run() {try {while(true){String line = br.readLine();if(line!=null) System.out.println(line);}} catch (IOException e) {throw new RuntimeException(e);}}
}
2.4服务端源码
package com.bluening.Server;import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*
*服务端程序
* */
public class Server {public static void main(String[] args) throws IOException {//1.读取本地文件中所有正确用户的信息ArrayList<String> userInfo = getUserInfo("Username_Password.txt");//2.创建ServerSocket对象,注意端口与客户端指定的端口保持一致ServerSocket serverSocket = new ServerSocket(10086);//3.服务端会被多个客户端连接,当用户数量过大时,单纯的循环效率低下//我们利用多线程来进行优化,但频繁地创建,销毁线程,对系统的开销比较大,我们可以利用线程池来进行优化//定义线程池ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,//核心线程的数量16,//最大线程数,不能小于0,最大数量>=核心线程数量60,//空闲线程最大存活时间TimeUnit.SECONDS,//时间单位new ArrayBlockingQueue<>(5),//任务队列Executors.defaultThreadFactory(),//创建线程工厂new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略);//由于涉及到服务端,向多个客户端转发信息,故而可以每个监听到的socket对象,利用集合来存储//由于用户分为登录和未登录两个状态,只有登录状态的用户才能收到服务器发送的聊天信息//所以我们应该用一个双列集合来存储信息//键为Socket,值为其登录状态码 1为登录,0为未登录HashMap<Socket,Integer> AllUserSocketMap = new HashMap<>();while(true){//监听客户端Socket socket = serverSocket.accept();//将socket对象加入到集合中//由于AllUserSocketMap对象有可能在线程中被修改,所以其为一个共享数据//在此利用同步代码块保证线程安全synchronized (Server.class){AllUserSocketMap.put(socket,0);}//监听到一个客户端则开启一条线程poolExecutor.submit(new ServerRunnable(socket,userInfo,AllUserSocketMap));}}//1.读取本地文件中所有正确用户的信息private static ArrayList<String> getUserInfo(String file) throws IOException {ArrayList<String> userInfo = new ArrayList<>();//字符缓冲输入流,可以用其中的readLine方法读取整行数据BufferedReader bw = new BufferedReader(new FileReader(file));//循环获取String line;while((line=bw.readLine())!=null){userInfo.add(line);}//释放资源bw.close();return userInfo;}
}
2.5服务端Runnable源码
package com.bluening.Server;import com.sun.source.tree.WhileLoopTree;import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*
* 执行线程的runnable类
* 功能:1.对登录用户进行校验
* 2.完成用户注册
* 3.用户聊天时将聊天信息转发给所有用户
* */
public class ServerRunnable implements Runnable{//显然要完成登录与注册功能需要,该条线程对应的Socket对象,用户信息的集合//完成聊天功能则需要所有客户端对应的socket对象集合//我们可以通过构造方法的方式,来从Server类中获取这些内容Socket socket;//本线程对应的socketString User; //若用户登录成功要记录其usernamestatic String Message; //群发的消息应该是共享的/** 值得注意的是,在此处userInfo,AllUserSocketMap是会发生变化的的数据* 每条线程都有可能对其进行增加,这说明userInfo,AllUserSocketMap是一个共享数据* 我们应该利用同步代码块,来保证线程安全* *///锁对象static final Object LOCK = new Object();//存放每个socket对象,对应的BufferedWriterstatic HashMap<Socket,BufferedWriter> AllSocketBufferedWriter = new HashMap<>();ArrayList<String> userInfo;//所有用户信息HashMap<Socket,Integer> AllUserSocketMap;//所有客户端对应的socket,与登录状态码//构造方法public ServerRunnable(Socket socket, ArrayList<String> userInfo, HashMap<Socket,Integer> AllUserSocketMaps) {this.socket = socket;this.userInfo = userInfo;this.AllUserSocketMap = AllUserSocketMaps;}@Overridepublic void run() {//服务端应该先判断当前的Client用户是否登录,如果登录已经登录,那么就只要群发信息即可//如果没有登录,则需要判断用户当前的行为是注册还是登录Integer status = AllUserSocketMap.get(socket);//获取状态码//字符输入流,准备读取客户端所发送来的信息BufferedReader br = null;try {br = new BufferedReader(new InputStreamReader(socket.getInputStream()));} catch (IOException e) {throw new RuntimeException(e);}//字符输出流,准备向客户端反馈信息BufferedWriter bw = null;try {bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));} catch (IOException e) {throw new RuntimeException(e);}//维护AllSocketBufferedWritersynchronized (LOCK) {AllSocketBufferedWriter.put(socket,bw);}//用于将数据写回文件BufferedWriter file_writer = null;try {//应该打开续写开关,不能清空文件file_writer = new BufferedWriter(new FileWriter("Username_Password.txt",true));} catch (IOException e) {throw new RuntimeException(e);}while(status==0){//表明用户未登录try {String line = br.readLine();String[] arr = line.split("&");if("login".equals(arr[2])){ //用户正在登录,应当对其账户密码进行验证String username = arr[0].split("=")[1];String password = arr[1].split("=")[1];String loginInfo = username+"="+password;//直接拼接成文件中信息的形式if(checkLogin(loginInfo)){//登录成功//修改当前socket状态码synchronized (LOCK) { //修改操作,应当保证共享数据安全AllUserSocketMap.put(socket,1);}status = AllUserSocketMap.get(socket);//向用户发送登录成功的信息bw.write("1"); //1表示登录成功bw.newLine();bw.flush(); // 刷新缓冲区确保数据发送//登录成功了,要记录当前socket对象的用户名User = username;}else { //登录失败bw.write("2"); //2表示登录失败bw.newLine();bw.flush();}}else if("register".equals(arr[2])){ //用户正在注册,应当对其账户密码进行验证String username = arr[0].split("=")[1];String password = arr[1].split("=")[1];if(usernameContains(username)){//用户名重复bw.write("2"); //2表示注册失败bw.newLine();bw.flush();}else {//注册成功,应该把数据写回文件,并且在集合中添加,这都是对共享数据的操作synchronized (LOCK) { //修改操作,应当保证共享数据安全file_writer.write(username+"="+password);// 使用系统默认的换行符file_writer.write(System.lineSeparator());file_writer.flush();userInfo.add(username+"="+password);}bw.write("1"); //1表示注册成功bw.newLine();bw.flush();}}} catch (IOException e) {throw new RuntimeException(e);}}//用户登录成功了,下面部分是群发消息的逻辑while (true) {try {Message = br.readLine();//发送逻辑Set<Map.Entry<Socket,Integer>> AllUserSocketentrySet = AllUserSocketMap.entrySet();for (Map.Entry<Socket, Integer> socketIntegerEntry : AllUserSocketentrySet) {if(socketIntegerEntry.getValue()==1){//如果已经登录//获取到对应socket对应的流BufferedWriter SocketBw = AllSocketBufferedWriter.get(socketIntegerEntry.getKey());synchronized (LOCK) {try {SocketBw.write(User+":"+Message);SocketBw.newLine();SocketBw.flush();} catch (IOException e) {throw new RuntimeException(e);}}}}} catch (IOException e) {throw new RuntimeException(e);}}}//给予注册模块使用,查看用户名是否已经存在private boolean usernameContains(String username) {for (String string : userInfo) {String name = string.split("=")[0];if(name.equals(username)) return true;}return false;}//给予登录模块使用,检查账户密码是否完全匹配private boolean checkLogin(String loginInfo) {for (String string : userInfo) {if(string.equals(loginInfo)) return true;}return false;}
}
服务端的基本逻辑是这样的
①先读取本地文件中所有正确用户信息
②当有客户端来连接时,就开启一条线程
③线程中判断当前用户是登录还是注册操作
④登录,校验用户名和密码是否正确
⑤注册,校验用户名是否唯一,校验用户名和密码格式
⑥如果登录成功,开始聊天
⑦如果注册成功,将用户信息写入到本地,开始聊天
1.首先如何判断用户是否登录了?
答:我的做法是这样的,我利用一个双列集合HashMap<Socket,Integer> AllUserSocketMaps来记录每个Socket对象的状态信息,0是未登录,1是登录
2.在编写服务端代码时,我遇到最严重的问题就是共享数据的混乱,但是我通过梳理思路,一步步解决了bug,在此我想记录我梳理的过程:
答:共享数据UserInfo和AllUserSocketMap是通过构造方法传过来的。
User变量是为了记录每个线程对应的用户名,方便群发消息的时候,知道是谁发的,所以User针对每个线程应该独立
Message则是需要被群发的消息,所以每个线程都需要,而且需要同一个,所以这显然是一个共享数据,我设置其为static,并且在对其修改值做了锁操作,保证线程安全。
此外我应该格外关注ServerRunnable中的IO流,这很关键,最简单的file_writer它是面向username_password.txt的输出流,于各个线程中独立存在,它应该没有什么问题。
其次就是两个针对Socket的流,br和bw,这两个流针对每个线程也是独立的,他们都是针对于自己线程的Socket对象
3.如何做到消息群发?
答:想做到消息群发,就要获取每个线程Socket对象对应的BufferedWriter对象,我是这样设计的,我创建了一个static的(各线程之间共享,所以这是一个共享数据) HashMap,键为Socket,值为对应的BufferedWriter,在群发消息时,我通过遍历AllSocketMap,如果状态码为1,则获取其BufferedWriter对消息进行发送。值得注意的是,这样每个线程的BufferedWriter就不仅是只会在本线程中用到了,故我要对BufferedWriter操作时,应当做同步处理。
3.项目心得
项目编码耗时大约10h左右,主要在线程中的排错比较耗费时间,常有思绪打结的情况,不过我通过梳理思路,也是逐一解决了。另外我在编程时写了必要的注释,进行了分模块的编程,使我修改起来更加方便,我还使用了git来进行版本管理,方便代码混乱时做版本穿梭,回溯版本。
相关文章:
Java网络编程,多线程,IO流综合项目一一ChatBoxes
Java网络编程,多线程,IO流综合小项目一一ChatBoxes 作者:blue 时间:2025.3.7 文章目录 Java网络编程,多线程,IO流综合小项目一一ChatBoxes1.项目介绍2.项目源码剖析2.1客户端源码2.2客户端Sender线程Runn…...
大数据、人工智能、云计算、物联网、区块链序言【大数据导论】
这里是阿川的博客,祝您变得更强 ✨ 个人主页:在线OJ的阿川 💖文章专栏:大数据入门到进阶 🌏代码仓库: 写在开头 现在您看到的是我的结论或想法,但在这背后凝结了大量的思考、经验和讨论 这是目…...
【算法 C/C++】一维前缀和
2025 - 03 - 08 - 第 68 篇 Author: 郑龙浩 / 仟濹 【一维前缀和】 文章目录 前缀和与差分 - 我的博客1 大体介绍2 计算某些区间的和( 不使用前缀和 )3 计算某些区间的和( 使用前缀和 ) 前缀和与差分 - 我的博客 一维前缀和 【算法 C/C】一维前缀和 一维差分 【算法 C/C】一维…...
【C++】:STL详解 —— 红黑树
目录 平衡二叉查找树 红黑树的概念 红黑树的五大性质 红黑树的效率 红黑树和AVL树的比较 插入与删除操作 内存与实现复杂度 经典性能数据对比 总结 对旋转的基本理解 旋转的作用 左旋(Left Rotation) 右旋(Right Rotation…...
【A2DP】SBC 编解码器互操作性要求详解
目录 一、SBC编解码器互操作性概述 二、编解码器特定信息元素(Codec Specific Information Elements) 2.1 采样频率(Sampling Frequency) 2.2 声道模式(Channel Mode) 2.3 块长度(Block Length) 2.4 子带数量(Subbands) 2.5 分配方法(Allocation Method) 2…...
Mysql的卸载安装配置以及简单使用
MySQL其它问题已经更新在:MySQL完善配置---可视化-CSDN博客 一、卸载 ①控制面板卸载 ②C盘隐藏项目>ProgramData>mysql相关文件夹,还有Program file下的MySQL文件夹 ③开始菜单栏搜索>服务,找到MySQL相关服务删除,如果再…...
Ubuntu 下 nginx-1.24.0 源码分析 (1)
main 函数在 src\core\nginx.c int ngx_cdecl main(int argc, char *const *argv) {ngx_buf_t *b;ngx_log_t *log;ngx_uint_t i;ngx_cycle_t *cycle, init_cycle;ngx_conf_dump_t *cd;ngx_core_conf_t *ccf;ngx_debug_init(); 进入 main 函数 最…...
驱动开发系列43 - Linux 显卡KMD驱动代码分析(四)- DRM设备操作
一:概述 DRM(Direct Rendering Manager)是Linux内核中的一个子系统,主要负责图形硬件的管理与图形渲染的加速。它为图形驱动提供了一个统一的接口,可以使用户空间程序与图形硬件进行直接交互,而无需通过X服务器或Wayland等显示管理器。DRM不仅用于管理显卡,还处理视频输…...
PAT乙级真题(2014·冬)
大纲 1031、查验身份证-(解析)-简单题 1032、挖掘机技术哪家强-(解析)-细节题(┬┬﹏┬┬),太抠细节了 1033、旧键盘打字-(解析)-输入格式!这才是重点(┬┬﹏┬┬),让…...
快速使用MASR V3版不能语音识别框架
前言 本文章主要介绍如何快速使用MASR语音识别框架训练和推理,本文将致力于最简单的方式去介绍使用,如果使用更进阶功能,还需要从源码去看文档。仅需三行代码即可实现训练和推理。 源码地址:https://github.com/yeyupiaoling/MA…...
学习笔记:Python网络编程初探之基本概念(一)
一、网络目的 让你设备上的数据和其他设备上进行共享,使用网络能够把多方链接在一起,然后可以进行数据传递。 网络编程就是,让在不同的电脑上的软件能够进行数据传递,即进程之间的通信。 二、IP地址的作用 用来标记唯一一台电脑…...
硬件基础(4):(2)认识ADC参考电压
文章目录 1. **ADC参考电压的定义**2. **如何影响采样值**3. **参考电压的选择**4. **如何选择参考电压**5. **总结** **ADC参考电压(Vref)**是用于定义ADC采样范围的一个重要参数,以下是对 ADC 参考电压的详细解释: 1. ADC参考电…...
项目中同时使用Redis(lettuce)和Redisson的报错
温馨提示:图片有点小,可以放大页面进行查看... 问题1:版本冲突 直接上图,这个错表示依赖版本不匹配问题,我本地SpringBoot用的是2.7,但是Redisson版本用的3.32.5。 我们通过点击 artifactId跟进去 发现它…...
工程化与框架系列(25)--低代码平台开发
低代码平台开发 🔧 引言 低代码开发平台是一种通过可视化配置和少量代码实现应用开发的技术方案。本文将深入探讨低代码平台的设计与实现,包括可视化编辑器、组件系统、数据流管理等关键主题,帮助开发者构建高效的低代码开发平台。 低代码…...
在CentOS系统上安装Conda的详细指南
前言 Conda 是一个开源的包管理系统和环境管理系统,广泛应用于数据科学和机器学习领域。本文将详细介绍如何在 CentOS 系统上安装 Conda,帮助您快速搭建开发环境。 准备工作 在开始安装之前,请确保您的 CentOS 系统已经满足以下条件&#x…...
系统思考—组织诊断
“未经过诊断的行动是盲目的。” — 托马斯爱迪生 最近和一家教育培训机构沟通时,发现他们面临一个有意思的问题:每年招生都挺不错,但教师的整体绩效一直提升缓慢,导致师生之间存在长期的不匹配。管理层试了很多办法,…...
项目实战--网页五子棋(对战功能)(9)
上期我们完成了websocket建立连接后的数据初始化,今天我们完成落子交互的具体代码: 这里我们先复习一下,之前约定好的落子请求与响应包含的字段: 1. 发送落子请求 我们在script.js文件中找到落子的相关方法,增加发送请…...
Ubuntu系统安装Apache2方法
Ubuntu系统安装Apache2方法 一、安装 Apache2更新软件包列表安装 Apache2启动服务验证安装 二、访问默认页面三、基本配置配置文件结构目录权限访问测试 四、故障排除五、总结 一、安装 Apache2 更新软件包列表 在安装任何软件之前,建议先更新系统的软件包列表&am…...
DeepSeek R1-32B医疗大模型的完整微调实战分析(全码版)
DeepSeek R1-32B微调实战指南 ├── 1. 环境准备 │ ├── 1.1 硬件配置 │ │ ├─ 全参数微调:4*A100 80GB │ │ └─ LoRA微调:单卡24GB │ ├── 1.2 软件依赖 │ │ ├─ PyTorch 2.1.2+CUDA │ │ └─ Unsloth/ColossalAI │ └── 1.3 模…...
基于springboot和spring-boot-starter-data-jpa快速操作mysql数据库
1、创建springboot项目 2、pom.xml文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http:…...
深度学习代码解读——自用
代码来自:GitHub - ChuHan89/WSSS-Tissue 借助了一些人工智能 2_generate_PM.py 功能总结 该代码用于 生成弱监督语义分割(WSSS)所需的伪掩码(Pseudo-Masks),是 Stage2 训练的前置步骤。其核心流程为&a…...
文件与目录权限
目录 文件权限 文件读权限(r) 文件写权限(w) 文件可执行权限(x) 目录权限 目录读权限(r) 目录写权限(w) 文件可执行权限(x)(与文件权限最大不同之处) 注意 在 Linux 系统中,…...
算法005——有效三角形个数
力扣——有效三角形个数点击链接跳转 判断三条边是否能组成三角形,大家第一时间想到的就是两边之和大于第三边 但是运用这个方法,我们需要判断三次,有一个更简单的方法,只需要判断一次 因为 C 已经是三边之中最大的了ÿ…...
Facebook 的隐私保护数据存储方案研究
Facebook 的隐私保护数据存储方案研究 在这个信息爆炸的时代,数据隐私保护已成为公众关注的热点。Facebook,作为全球最大的社交媒体平台之一,承载着海量用户数据,其隐私保护措施和数据存储方案对于维护用户隐私至关重要。本文将深…...
如何高效利用Spring中的@Cacheable注解?
在现代软件开发中,缓存是提升应用性能的重要手段。Spring框架提供了Cacheable注解,帮助开发者轻松实现缓存机制。今天我们就来详细聊聊Cacheable注解的使用,看看它是如何让我们的应用更加高效的! Cacheable注解的核心功能是缓存方…...
Qt 进度条与多线程应用、基于 Qt 的文件复制工具开发
练习1:Qt 进度条与多线程应用 题目描述 开发一个基于 Qt 的应用程序,该应用程序包含一个水平进度条(QSlider),并且需要通过多线程来更新进度条的值。请根据以下要求完成代码: 界面设计: 使用 QS…...
软件工程---构件
在软件工程中,构件是一个独立的、可复用的软件单元,它具有明确的功能、接口和行为,并且可以在不同的环境中加以集成和复用。构件的概念是软件架构和组件化开发的核心思想之一,其目的是促进软件系统的模块化、可维护性和可扩展性。…...
【算法 C/C++】二维差分
2025 - 03 - 08 - 第 71 篇 Author: 郑龙浩 / 仟濹 【二维差分】 文章目录 前缀和与差分 - 我的博客差分(二维)1 大体思路 | 一些区间加某数的最终结果2 二维差分原理3 例题 前缀和与差分 - 我的博客 一维前缀和 【算法 C/C】一维前缀和 一维差分 【算法 C/C】一维差分 二维前…...
灰色地带规避:知识产权校验API的商标库模糊匹配算法
在反向海淘或其他电商业务场景中,为了规避知识产权方面的灰色地带,开发知识产权校验 API 并运用商标库模糊匹配算法是很有必要的。以下将详细介绍商标库模糊匹配算法的设计与实现: 算法设计思路 商标库模糊匹配算法的核心目标是在给定一个待匹…...
LINUX网络基础 [五] - HTTP协议
目录 HTTP协议 预备知识 认识 URL 认识 urlencode 和 urldecode HTTP协议格式 HTTP请求协议格式 HTTP响应协议格式 HTTP的方法 HTTP的状态码 编辑HTTP常见Header HTTP实现代码 HttpServer.hpp HttpServer.cpp Socket.hpp log.hpp Makefile Web根目录 H…...
嵌入式人工智能应用-第6章 人脸检测
嵌入式人工智能应用 人脸检测 嵌入式人工智能应用1 人脸检测1.1 CNN 介绍1.2 人脸检测原理1.3 MTCNN介绍1.4 NCNN介绍2 系统安装2.1 安装依赖库NCNN2.2 运行对应的库3 总结1 人脸检测 1.1 CNN 介绍 卷积神经网络。卷积是什么意思呢?从数学上说,卷积是一种运算。它是我们学习…...
编程考古-Borland历史:《.EXE Interview》对Anders Hejlsberg关于Delphi的采访内容(中)
为了纪念Delphi在2002年2月14日发布的25周年(2020.2.12),这里有一段由.EXE杂志编辑Will Watts于1995年对Delphi首席架构师Anders Hejlsberg进行的采访记录。在这次采访中,Anders讨论了Delphi的设计与发展,以及即将到来的针对Windows 95的32位版本。 Q. 编译器引擎本身是用…...
redis数据类型以及底层数据结构
redis数据类型以及底层数据结构 String:字符串类型,底层就是动态字符串,使用sds数据结构 Map:有两种数据结构:1.压缩列表:当hash结构中存储的元素个数小于了512个。并且元 …...
C运算符 对比a++、++a、b--、 --b
#include<stdio.h> int main() { int a 21;int b 10;int c, d;c a;//先赋值给c,a本身再运算 c 21, a 22;//c a;//a本身先运算,再赋值给c a 22,c 22;printf("c %d, a %d\n",c, a); d --b;//b本身先运算,再赋值给d …...
Java EE 进阶:Spring MVC(2)
cookie和session的关系 两者都是在客户端和服务器中进行存储数据和传递信息的工具 cookie和session的区别 Cookie是客⼾端保存⽤⼾信息的⼀种机制. Session是服务器端保存⽤⼾信息的⼀种机制. Cookie和Session之间主要是通过SessionId关联起来的,SessionId是Co…...
基于Matlab的人脸识别的二维PCA
一、基本原理 传统 PCA 在处理图像数据时,需将二维图像矩阵拉伸为一维向量,这使得数据维度剧增,引发高计算成本与存储压力。与之不同,2DPCA 直接基于二维图像矩阵展开运算。 它着眼于图像矩阵的列向量,构建协方差矩阵…...
Java 深度复制对象:从基础到实战
目录 一、深度复制的概念二、实现深度复制的方法1. 使用序列化2. 手动实现深度复制 三、总结 在 Java 编程中,对象的复制是一个常见的需求。然而,简单的复制操作(如直接赋值)只会复制对象的引用,而不是创建一个新的对象…...
【Java开发指南 | 第三十五篇】Maven + Tomcat Web应用程序搭建
读者可订阅专栏:Java开发指南 |【CSDN秋说】 文章目录 前言Maven Tomcat Web应用程序搭建1、使用Maven构建新项目2、单击项目,连续按两次shift键,输入"添加",选择"添加框架支持"3、选择Java Web程序4、点击&…...
TCP三次握手,四次挥手;多进程、多线程实现并发服务器
三次握手,四次挥手 三次握手示意图: SYN、ACK是TCP协议头里面的标志位 同步 SYN:仅在三次握手建立 TCP 连接时有效。当 SYN 1 而 ACK 0 时,表明这是一个连接请求报文段,对方若同意建立连接,则应在相应的…...
Java基础系列:深入理解八大基本数据类型及避坑指南
目录 一、基本数据类型概述 八大类型速查表 二、各类型详解与常见陷阱 1. 整型家族(byte/short/int/long) 2. 浮点型(float/double) 3. 字符型(char) 4. 布尔型(boolean) 三…...
【Gaussian Model】高斯分布模型
目录 高斯分布模型用于异常检测(Gaussian Model for Anomaly Detection)1. 高斯分布简介2. 高斯分布模型用于异常检测(1) 训练阶段:估计数据分布(2) 检测阶段:计算概率判断异常点 3. 示例代码4. 高斯分布异常检测的优缺点优点缺点…...
Unity--Cubism Live2D模型使用
了解LIVE2D在unity的使用--前提记录 了解各个组件的作用 Live2D Manuals & Tutorials 这些文件都是重要的控制动画参数的 Cubism Editor是编辑Live2D的工具,而导出的数据的类型,需要满足以上的条件 SDK中包含的Cubism的Importer会自动生成一个Pref…...
Day4 C语言与画面显示练习
文章目录 1. harib01a例程2. harib01b例程3. harib01e例程4. harib01f例程5. harib01h例程 1. harib01a例程 上一章主要是将画面搞成黑屏,如果期望做点什么图案,只需要再VRAM里写点什么就好了,使用nask汇编语言实现一个函数write_mem8&#…...
【redis】全局命令exists、del、expire、ttl(惰性删除和定期删除)
exists——判定 key 是否存在 语法: exists key [key...] # 返回值:key 存在的个数针对多个 key 来说,是非常有用的时间复杂度 O ( 1 ) O(1) O(1) Redis 组织这些 key 就是按照哈希表的方式来组织的。Redis 支持很多数据结构指的是 value …...
VUE3项目的文档结构分析
1. Vue 3 项目的文档结构 Vue 3 项目通常基于 Vue CLI 或 Vite 等工具创建,其文档结构如下: 常见目录结构 my-vue-project/ ├── public/ # 静态资源目录 │ ├── index.html # 入口页面 ├── src/ …...
Linux笔记---自定义shell
目录 前言 1. 程序框架 2. 打印命令行提示符 2.1 获取用户名(GetUserName) 2.2 获取主机名(GetHostName) 2.3 获取工作目录(GetPwd) 3. 获取命令行输入 4. 判断是否有重定向 5. 解析命令行 6. 内建命令 6.1 内建命令的特点 6.2 常见内建命令 6.3 内建命令 vs 外部命…...
步进电机软件细分算法解析与实践指南
1. 步进电机细分技术概述 步进电机是一种将电脉冲信号转换为角位移的执行机构,其基本运动单位为步距角。传统步进电机的步距角通常为 1.8(对应 200 步 / 转),但在高精度定位场景下,这种分辨率已无法满足需求。细分技术…...
mapbox开发小技巧
自定义图标 // 1、单个图标 const url ./static/assets/symbols/code24x24/VIDEO.png // 图标路径 map.loadImage(url ,(error, image) > {if (error) throw errormap.addImage(video-icon, image) })// 2、雪碧图利用canvas // json和png图片 function getStyleImage(fil…...
ApoorvCTF Rust语言逆向实战
上周参加了国外的比赛,名称叫:ApoorvCTF 看一下老外的比赛跟我们有什么不同,然后我根据国内比赛对比发现,他们考点还是很有意思的,反正都是逆向,哈哈哈 Rusty Vault 题目描述: In the heart…...
IntersectionObserver接口介绍
IntersectionObserver API 是浏览器提供的一个用于异步观察目标元素与其祖先元素或视口(Viewport)交叉状态(即是否进入或离开视口)的接口。在 IntersectionObserver 出现之前,开发者通常需要通过监听 scroll 事件或使用…...