TCP通信实战案例-即时通信
即时通信是什么含义,要实现怎么样的设计?
即时通信,是指一个客户端的消息发出去,其他客户端可以接收到。
即时通信需要进行端口转发的设计思想。
服务端需要把在线的Socket管道存储起来。
一旦收到一个消息要推送给其他管道。
即时通信-端口转发
代码实现
/**拓展:即时通信客户端:发消息的同时,随时有人发消息过来。服务端:接收消息后,推送给其他所有的在线socket*/
public class ClientDemo1 {public static void main(String[] args) {try {System.out.println("====客户端启动===");// 1、创建Socket通信管道请求有服务端的连接// public Socket(String host, int port)// 参数一:服务端的IP地址// 参数二:服务端的端口Socket socket = new Socket("127.0.0.1", 6868);// 马上为客户端分配一个独立的线程负责读取它收到的消息new ClientReaderThread(socket).start();// 2、从socket通信管道中得到一个字节输出流 负责发送数据OutputStream os = socket.getOutputStream();// 3、把低级的字节流包装成打印流PrintStream ps = new PrintStream(os);Scanner sc = new Scanner(System.in);while (true) {System.out.println("请说:");String msg = sc.nextLine();// 4、发送消息ps.println(msg);ps.flush();}// 关闭资源。// socket.close();} catch (Exception e) {e.printStackTrace();}}
}
public class ClientReaderThread extends Thread{private Socket socket;public ClientReaderThread(Socket socket){this.socket = socket;}@Overridepublic void run() {try {// 3、从socket通信管道中得到一个字节输入流InputStream is = socket.getInputStream();// 4、把字节输入流包装成缓冲字符输入流进行消息的接收BufferedReader br = new BufferedReader(new InputStreamReader(is));// 5、按照行读取消息String msg;while ((msg = br.readLine()) != null){System.out.println(socket.getRemoteSocketAddress() + "收到了: " + msg);}} catch (Exception e) {System.out.println("服务端把你踢出去了~~");}}
}
/**目标: 即时通信*/
public class ServerDemo2 {public static List<Socket> onLineSockets = new ArrayList<>();public static void main(String[] args) {try {System.out.println("===服务端启动成功===");// 1、注册端口ServerSocket serverSocket = new ServerSocket(6868);// a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。while (true) {// 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息Socket socket = serverSocket.accept();System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!");// 把当前客户端管道Socket加入到在线集合中去onLineSockets.add(socket);// 3、开始创建独立线程处理socketnew ServerReaderThread(socket).start();}} catch (Exception e) {e.printStackTrace();}}
}
public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(Socket socket){this.socket = socket;}@Overridepublic void run() {try {// 3、从socket通信管道中得到一个字节输入流InputStream is = socket.getInputStream();// 4、把字节输入流包装成缓冲字符输入流进行消息的接收BufferedReader br = new BufferedReader(new InputStreamReader(is));// 5、按照行读取消息String msg;while ((msg = br.readLine()) != null){System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg);// 把这个消息发给当前所有在线socketsendMsgToAll(msg);}} catch (Exception e) {System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");// 从在线集合中抹掉本客户端socketServerDemo2.onLineSockets.remove(socket);}}private void sendMsgToAll(String msg) {try {// 遍历全部的在线 socket给他们发消息for (Socket onLineSocket : ServerDemo2.onLineSockets) {// 除了自己的socket,其他socket我都发!!if(onLineSocket != socket){PrintStream ps = new PrintStream(onLineSocket.getOutputStream());ps.println(msg);ps.flush();}}} catch (Exception e) {e.printStackTrace();}}
}
简易聊天系统代码
package com.itheima.d9_chat.即时通信;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;/*** @Author xlei(徐磊)* 客户端界面*/
public class ClientChat implements ActionListener {/** 1.设计界面 */private JFrame win = new JFrame();/** 2.消息内容框架 */public JTextArea smsContent =new JTextArea(23 , 50);/** 3.发送消息的框 */private JTextArea smsSend = new JTextArea(4,40);/** 4.在线人数的区域 *//** 存放人的数据 *//** 展示在线人数的窗口 */public JList<String> onLineUsers = new JList<>();// 是否私聊按钮private JCheckBox isPrivateBn = new JCheckBox("私聊");// 消息按钮private JButton sendBn = new JButton("发送");// 登录界面private JFrame loginView;private JTextField ipEt , nameEt , idEt;private Socket socket ;public static void main(String[] args) {new ClientChat().initView();}private void initView() {/** 初始化聊天窗口的界面 */win.setSize(650, 600);/** 展示登录界面 */displayLoginView();/** 展示聊天界面 *///displayChatView();}private void displayChatView() {JPanel bottomPanel = new JPanel(new BorderLayout());//-----------------------------------------------// 将消息框和按钮 添加到窗口的底端win.add(bottomPanel, BorderLayout.SOUTH);bottomPanel.add(smsSend);JPanel btns = new JPanel(new FlowLayout(FlowLayout.LEFT));btns.add(sendBn);btns.add(isPrivateBn);bottomPanel.add(btns, BorderLayout.EAST);//-----------------------------------------------// 给发送消息按钮绑定点击事件监听器// 将展示消息区centerPanel添加到窗口的中间smsContent.setBackground(new Color(0xdd,0xdd,0xdd));// 让展示消息区可以滚动。win.add(new JScrollPane(smsContent), BorderLayout.CENTER);smsContent.setEditable(false);//-----------------------------------------------// 用户列表和是否私聊放到窗口的最右边Box rightBox = new Box(BoxLayout.Y_AXIS);onLineUsers.setFixedCellWidth(120);onLineUsers.setVisibleRowCount(13);rightBox.add(new JScrollPane(onLineUsers));win.add(rightBox, BorderLayout.EAST);//-----------------------------------------------// 关闭窗口退出当前程序win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);win.pack(); // swing 加上这句 就可以拥有关闭窗口的功能/** 设置窗口居中,显示出来 */setWindowCenter(win,650,600,true);// 发送按钮绑定点击事件sendBn.addActionListener(this);}private void displayLoginView(){/** 先让用户进行登录* 服务端ip* 用户名* id* *//** 显示一个qq的登录框 */loginView = new JFrame("登录");loginView.setLayout(new GridLayout(3, 1));loginView.setSize(400, 230);JPanel ip = new JPanel();JLabel label = new JLabel(" IP:");ip.add(label);ipEt = new JTextField(20);ip.add(ipEt);loginView.add(ip);JPanel name = new JPanel();JLabel label1 = new JLabel("姓名:");name.add(label1);nameEt = new JTextField(20);name.add(nameEt);loginView.add(name);JPanel btnView = new JPanel();JButton login = new JButton("登陆");btnView.add(login);JButton cancle = new JButton("取消");btnView.add(cancle);loginView.add(btnView);// 关闭窗口退出当前程序loginView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setWindowCenter(loginView,400,260,true);/** 给登录和取消绑定点击事件 */login.addActionListener(this);cancle.addActionListener(this);}private static void setWindowCenter(JFrame frame, int width , int height, boolean flag) {/** 得到所在系统所在屏幕的宽高 */Dimension ds = frame.getToolkit().getScreenSize();/** 拿到电脑的宽 */int width1 = ds.width;/** 高 */int height1 = ds.height ;System.out.println(width1 +"*" + height1);/** 设置窗口的左上角坐标 */frame.setLocation(width1/2 - width/2, height1/2 -height/2);frame.setVisible(flag);}@Overridepublic void actionPerformed(ActionEvent e) {/** 得到点击的事件源 */JButton btn = (JButton) e.getSource();switch(btn.getText()){case "登陆":String ip = ipEt.getText().toString();String name = nameEt.getText().toString();// 校验参数是否为空// 错误提示String msg = "" ;// 12.1.2.0// \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\if(ip==null || !ip.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){msg = "请输入合法的服务端ip地址";}else if(name==null || !name.matches("\\S{1,}")){msg = "姓名必须1个字符以上";}if(!msg.equals("")){/** msg有内容说明参数有为空 */// 参数一:弹出放到哪个窗口里面JOptionPane.showMessageDialog(loginView, msg);}else{try {// 参数都合法了// 当前登录的用户,去服务端登陆/** 先把当前用户的名称展示到界面 */win.setTitle(name);// 去服务端登陆连接一个socket管道socket = new Socket(ip, Constants.PORT);//为客户端的socket分配一个线程 专门负责收消息new ClientReader(this,socket).start();// 带上用户信息过去DataOutputStream dos = new DataOutputStream(socket.getOutputStream());dos.writeInt(1); // 登录消息dos.writeUTF(name.trim());dos.flush();// 关系当前窗口 弹出聊天界面loginView.dispose(); // 登录窗口销毁displayChatView(); // 展示了聊天窗口了} catch (Exception e1) {e1.printStackTrace();}}break;case "取消":/** 退出系统 */System.exit(0);break;case "发送":// 得到发送消息的内容String msgSend = smsSend.getText().toString();if(!msgSend.trim().equals("")){/** 发消息给服务端 */try {// 判断是否对谁发消息String selectName = onLineUsers.getSelectedValue();int flag = 2 ;// 群发 @消息if(selectName!=null&&!selectName.equals("")){msgSend =("@"+selectName+","+msgSend);/** 判断是否选中了私法 */if(isPrivateBn.isSelected()){/** 私法 */flag = 3 ;//私发消息}}DataOutputStream dos = new DataOutputStream(socket.getOutputStream());dos.writeInt(flag); // 群发消息 发送给所有人dos.writeUTF(msgSend);if(flag == 3){// 告诉服务端我对谁私发dos.writeUTF(selectName.trim());}dos.flush();} catch (Exception e1) {e1.printStackTrace();}}smsSend.setText(null);break;}}
}class ClientReader extends Thread {private Socket socket;private ClientChat clientChat ;public ClientReader(ClientChat clientChat, Socket socket) {this.clientChat = clientChat;this.socket = socket;}@Overridepublic void run() {try {DataInputStream dis = new DataInputStream(socket.getInputStream());/** 循环一直等待客户端的消息 */while(true){/** 读取当前的消息类型 :登录,群发,私聊 , @消息 */int flag = dis.readInt();if(flag == 1){// 在线人数消息回来了String nameDatas = dis.readUTF();// 展示到在线人数的界面String[] names = nameDatas.split(Constants.SPILIT);clientChat.onLineUsers.setListData(names);}else if(flag == 2){// 群发消息String msg = dis.readUTF() ;clientChat.smsContent.append(msg);//滾動到底端clientChat.smsContent.setCaretPosition(clientChat.smsContent.getText().length());}}} catch (Exception e) {e.printStackTrace();}}
}
package com.itheima.d9_chat.即时通信;public class Constants {/** 常量 */public static final int PORT = 7778 ;/** 协议分隔符 */public static final String SPILIT = "003197♣♣㏘♣④④♣";
}
package com.itheima.d9_chat.即时通信;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*** @Author* @Email dlei0009@163.com*/
public class ServerChat {/** 定义一个集合存放所有在线的socket */public static Map<Socket, String> onLineSockets = new HashMap<>();public static void main(String[] args) {try {/** 注册端口 */ServerSocket serverSocket = new ServerSocket(Constants.PORT);/** 循环一直等待所有可能的客户端连接 */while(true){Socket socket = serverSocket.accept();/** 把客户端的socket管道单独配置一个线程来处理 */new ServerReader(socket).start();}} catch (Exception e) {e.printStackTrace();}}
}class ServerReader extends Thread {private Socket socket;public ServerReader(Socket socket) {this.socket = socket;}@Overridepublic void run() {DataInputStream dis = null;try {dis = new DataInputStream(socket.getInputStream());/** 循环一直等待客户端的消息 */while(true){/** 读取当前的消息类型 :登录,群发,私聊 , @消息 */int flag = dis.readInt();if(flag == 1){/** 先将当前登录的客户端socket存到在线人数的socket集合中 */String name = dis.readUTF() ;System.out.println(name+"---->"+socket.getRemoteSocketAddress());ServerChat.onLineSockets.put(socket, name);}writeMsg(flag,dis);}} catch (Exception e) {System.out.println("--有人下线了--");// 从在线人数中将当前socket移出去 ServerChat.onLineSockets.remove(socket);try {// 从新更新在线人数并发给所有客户端 writeMsg(1,dis);} catch (Exception e1) {e1.printStackTrace();}}}private void writeMsg(int flag, DataInputStream dis) throws Exception {
// DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); // 定义一个变量存放最终的消息形式 String msg = null ;if(flag == 1){/** 读取所有在线人数发给所有客户端去更新自己的在线人数列表 *//** onlineNames = [徐磊,zhangsan,李刚]*/StringBuilder rs = new StringBuilder();Collection<String> onlineNames = ServerChat.onLineSockets.values();// 判断是否存在在线人数 if(onlineNames != null && onlineNames.size() > 0){for(String name : onlineNames){rs.append(name+ Constants.SPILIT);}// 徐磊003197♣♣㏘♣④④♣zhangsan003197♣♣㏘♣④④♣李刚003197♣♣㏘♣④④♣// 去掉最后的一个分隔符 msg = rs.substring(0, rs.lastIndexOf(Constants.SPILIT));/** 将消息发送给所有的客户端 */sendMsgToAll(flag,msg);}}else if(flag == 2 || flag == 3){// 读到消息 群发的 或者 @消息String newMsg = dis.readUTF() ; // 消息// 得到发件人 String sendName = ServerChat.onLineSockets.get(socket);// 李刚 时间// 内容--StringBuilder msgFinal = new StringBuilder();// 时间 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE");if(flag == 2){msgFinal.append(sendName).append(" ").append(sdf.format(System.currentTimeMillis())).append("\r\n");msgFinal.append(" ").append(newMsg).append("\r\n");sendMsgToAll(flag,msgFinal.toString());}else if(flag == 3){msgFinal.append(sendName).append(" ").append(sdf.format(System.currentTimeMillis())).append("对您私发\r\n");msgFinal.append(" ").append(newMsg).append("\r\n");// 私发 // 得到给谁私发 String destName = dis.readUTF();sendMsgToOne(destName,msgFinal.toString());}}}/*** @param destName 对谁私发 * @param msg 发的消息内容 * @throws Exception*/private void sendMsgToOne(String destName, String msg) throws Exception {// 拿到所有的在线socket管道 给这些管道写出消息Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();for(Socket sk : allOnLineSockets){// 得到当前需要私发的socket // 只对这个名字对应的socket私发消息if(ServerChat.onLineSockets.get(sk).trim().equals(destName)){DataOutputStream dos = new DataOutputStream(sk.getOutputStream());dos.writeInt(2); // 消息类型dos.writeUTF(msg);dos.flush();}}}private void sendMsgToAll(int flag, String msg) throws Exception {// 拿到所有的在线socket管道 给这些管道写出消息Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();for(Socket sk : allOnLineSockets){DataOutputStream dos = new DataOutputStream(sk.getOutputStream());dos.writeInt(flag); // 消息类型dos.writeUTF(msg);dos.flush();}}
}
package com.itheima.d9_chat.即时通信;public class User {private Integer id ;private String name ;public User(Integer id, String name) {this.id = id;this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User [id=" + id + ", name=" + name + "]";}
}
相关文章:

TCP通信实战案例-即时通信
即时通信是什么含义,要实现怎么样的设计? 即时通信,是指一个客户端的消息发出去,其他客户端可以接收到。 即时通信需要进行端口转发的设计思想。 服务端需要把在线的Socket管道存储起来。 一旦收到一个消息要推送给其他管道。…...

【数据结构初阶】算法的时间复杂度和空间复杂度
算法的时间复杂度和空间复杂度 1.算法效率1.1 如何衡量一个算法的好坏1.2 算法的复杂度 2.时间复杂度2.1 时间复杂度的概念2.2 大O的渐进表示法2.3常见时间复杂度计算举例 3.空间复杂度4. 常见复杂度对比 1.算法效率 1.1 如何衡量一个算法的好坏 如何衡量一个算法的好坏呢&am…...

git log 命令详解
测试仓库 asdf 常用参数 查询指定目录 git -C /Users/yanlp/workspace/asdf log 限制显示提交数量 git log -n 3 限制提交人|邮箱 git log --authorEdwin Kofler | git log --authoredwinkofler.dev 限制一个月内的提交git log --since1.month.ago | git log --since2023-0…...
docker运行elastic和kibana,并使用密码连接
1. elasticsearch 运行容器 docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.typesingle-node" elasticsearch:7.7.0 进入容器 docker exec -it elasticsearch bash修改配置开启密码校验 vi ./conf/elasticsearch.yml添加以下内容 …...
前端html生成PDF
需要用到的组件 1、html2canvas 地址:http://html2canvas.hertzen.com/ 安装:npm install --save html2canvas 2、jsPDF 地址:https://github.com/parallax/jsPDF 安装:npm install jspdf --save 代码 // 导出页面为PDF格式 im…...

强化学习代码实战(3) --- 寻找真我
前言 本文内容来自于南京大学郭宪老师在博文视点学院录制的视频,课程仅9元地址,配套书籍为深入浅出强化学习 编程实战 郭宪地址。 正文 我们发现多臂赌博机执行一个动作之后,无论是选择摇臂1,摇臂2,还是摇臂3之后都会返…...
SA+ST表维护height+单调队列维护:CF1073G
https://www.luogu.com.cn/problem/CF1073G lcp相关的,先跑个sa,然后height建个ST表 现在考虑询问,我们按A和B按 r k rk rk 排序。现在考虑B->A,反过来同理。 我们可以用单调队列维护,满足height是单增的。因为…...

Java中JVM、JRE和JDK三者有什么区别和联系?
任何语言或者软件的运行都需要环境。就像人要生活在空气中,鱼要活在水中,喜阴植物就不能放在阳光下暴晒一样,任何对象个体的存在都离不开其所需要的环境,编程语言亦是一样的。 java 语言的开发运行,也离不开 Java 语言…...

秋季期中考复现xj
flow analysis 1 What is the backdoor file name that comes with the server?( Including file suffix) 服务器自带的后门文件是什么?(含文件后缀) 题目还要求最后把那个文件名MD5一下,再去提交 开始的前三题是流量分析的&…...
【代码随想录】算法训练营 第十四天 第六章 二叉树 Part 1
递归遍历 递归法讲究的就是一个格式,在外边再定义一个用于递归求解的函数reverser,参数是递归的二叉树当前根节点和用于保存遍历得到的答案序列的vector容器; 函数中的格式就是,先写递归终止条件,也就是遍历的结点为…...

【访问控制】—>《熟练使用ACL进行上网行为管理》
✍ 标准和高级ACL功能介绍; ✍ 思科和华为ACL功能有什么区别? ✍ 现网中ACL都有哪些使用场景? -- ACL - 访问控制列表 - 控制: 能通/不能通 -- ACL - 结合功能 list - 简化版本的行为管理 -- 插件性质的功能 --…...
MySQL外键,表与表的关系,多表查询,Navicat软件
外键 MySQL可以使用外键来保持表之间的关系完整性。 要设置外键,可以按照以下步骤进行操作: 在创建表时,使用FOREIGN KEY关键字来指定外键列: CREATE TABLE table1 (id INT PRIMARY KEY,name VARCHAR(50),table2_id INT,FOREI…...
Linux系统镜像备忘
阿里镜像源: ubuntu ubuntu-releases安装包下载_开源镜像站-阿里云 centos centos-stream安装包下载_开源镜像站-阿里云...
Docker容器端口在主机的映射
Docker容器端口在主机的映射 Docker 允许你在启动容器时进行多个端口映射,以便将容器内部的端口映射到宿主机上的不同端口。你可以使用-p或--publish标志来指定端口映射。以下是一些示例,说明如何在 Docker 启动容器时进行多个端口映射: 映…...

Spring Boot中RedisTemplate的使用
当前Spring Boot的版本为2.7.6,在使用RedisTemplate之前我们需要在pom.xml中引入下述依赖: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><vers…...
GSCoolink GSV6183 带嵌入式MCU的MIPI D-PHY 转 DP/eDP
Gscoolink GSV6183是一款高性能、低功耗的MIPI D-PHY到DisplayPort/eDP 1.4转换器。通过集成基于RISC-V的增强型微控制器,GSV6183创造了一种具有成本效益的解决方案,提供了上市时间优势。MIPI D-PHY接收器支持CSI-2版本1.3和DSI版本1.3,每条通…...

Linux文件系统 struct dentry 结构体解析
文章目录 前言一、目录项简介二、struct dentry2.1 简介2.2 dentry和inode关联2.3 目录项视图2.4 目录项状态2.5 目录项特点 三、dentry cache3.1 简介3.2 dentry cache 初始化3.3 dentry cache 查看 四、dentry与mount、file的关联五、其他参考资料 前言 这两篇文章介绍了: V…...

C++——vector
目录 vector vector常用接口 构造函数 operator[]size() 迭代器 范围for capacity() resize() reverse() push_back和pop_back insert erase() algorithm::sort 注意 迭代器失效 vector vector单词直译是向量的意思,这个容器可以容纳不同的类型数据&am…...

html5语义化标签
目录 前言 什么是语义化标签 常见的语义化标签 语义化的好处 前言 HTML5 的设计目的是为了在移动设备上支持多媒体。之前网页如果想嵌入视频音频,需要用到 flash ,但是苹果设备是不支持 flash 的,所以为了改变这一现状,html5 …...

YOLO在QT中的完整训练、验证与部署方案
以下是YOLO在QT中的完整训练、验证与部署方案: 训练方案 准备数据集: 收集数据:收集与目标检测任务相关的图像数据集,可以是公开数据集如COCO、Pascal VOC,也可以是自定义数据集。标注数据:使用标注工具如…...

chili3d 笔记17 c++ 编译hlr 带隐藏线工程图
这个要注册不然emscripten编译不起来 --------------- 行不通 ---------------- 结构体 using LineSegment std::pair<gp_Pnt, gp_Pnt>;using LineSegmentList std::vector<LineSegment>; EMSCRIPTEN_BINDINGS(Shape_Projection) {value_object<LineSegment&g…...

网络寻路--图论
所以我们固定题中M条边(因为这M条一定联通) P8605 [蓝桥杯 2013 国 AC] 网络寻路 - 洛谷 #include<bits/stdc.h> using namespace std; #define N 100011 typedef long long ll; typedef pair<int,int> pii; int n,m; int d[N],u[N],v[N]…...
tpc udp http
TCP(传输控制协议)、UDP(用户数据报协议)和 HTTP(超文本传输协议)是网络通信中常用的三种协议,它们在不同的层次和场景中发挥作用。以下是对这三种协议的详细解释以及它们之间的区别:…...
AndroidR车机TextToSpeech音频焦点异常问题分析
一、引言 文章《Android车机之TextToSpeech》介绍了TextToSpeech的使用,当前较多座舱系统语音服务都接入了原生TextToSpeech接口调用。 我司自研语音TTS服务,也接入了此TTS接口调用,对外提供TextToSpeech能力,播报时由客户端Client自行管理音频焦点,播报前申请音频焦点,…...
Druid连接池实现自定义数据库密码加解密功能详解
Druid连接池实现自定义数据库密码加解密功能详解 在企业级应用开发中,数据库密码的明文存储是一个显著的安全隐患。Druid作为阿里巴巴开源的高性能数据库连接池组件,提供了灵活的密码加密与解密功能,允许开发者通过自定义逻辑实现数据库密码…...
python八股文算法:三数之和
双指针解法: 原理见注释 # 2025/6/6 9:40 # -*- coding:UTF-8 -*- nums [-1, 0, 1,1, 2, -1, -4,0,2,1,-3,4,10,-9] def three_sum(nums):nums.sort()n len(nums)result []for i in range(n-2):# n-2,此时i取值到n-2-1,即倒数第3个数&…...
C#中的路由事件(Routed Events)
路由事件的基本概念 路由事件是WPF中特有的事件系统,它允许事件在可视化树中"路由"传递,具有以下特点: 事件路由方向: 冒泡(Tunneling):从事件源向根元素传递 隧道(Bubbling):从根元素向事件源…...

视觉分析在人员行为属性检测中的应用
基于视觉分析的人员行为属性检测方案 一、背景与需求分析 在工业生产、建筑施工、公共安全等领域,人员行为属性的合规性检测是保障安全生产的关键环节。例如,工地工人未佩戴安全帽、厨房人员未佩戴手套、作业现场人员使用手机等行为,均可能…...

Verilog编程技巧01——如何编写三段式状态机
前言 Verilog编程技巧系列文章将聚焦于介绍Verilog的各种编程范式或者说技巧,编程技巧和编程规范有部分重合,但并非完全一样。规范更注重编码的格式,像变量命名、缩进、注释风格等,而编程技巧则更偏重更直观易读、更便于维护、综合…...