websocket实现
由于安卓资源管理器展示的路径不尽相同,各种软件保存文件的位置也不一定一样.对于普通用户上传文件时,查找文件可能是一个麻烦的事情.后来想到了一个办法,使用pc端进行辅助上传.
文章目录
- 实现思路
- 1.0 实现
- 定义web与客户端通信数据类型和数据格式
- web端websocket实现
- web端对客户端数据的管理
- pc端实现
- OkHttp3建立websocket连接
- 2.0版本
- spring-boot放到nginx后面
- spring-boot 放到gateway后面
- spring-boot 放到nginx gateway后面
- ws升级为wss
- 其他
- springboot打包
实现思路
- pc端与服务器建立websocket连接;
- 服务器将sessionId传递到pc端;
- pc端生成二维码;
- 手机端扫描二维码,读取pc端sessionId;
- 手机端与服务器建立websocket连接;
- 手机端将fileId(后面再解释)、pc端sessionId、token等参数传递给服务器;
- 服务器更新pc端session 对应的fileId;
- 服务器将fileId、token等发送到pc端;
- pc使用token、fileId等请求文件列表并进行展示;
- 手机端、pc端进行文件修改后,向服务器发送给更新信号,服务器将更新信号转发到对端。
1.0 实现
定义web与客户端通信数据类型和数据格式
- 定义web与客户端通信数据类型
public class MsgType {public static final int UPDATE = 0; //提示客户端数据发生更新public static final int REQ = 1; //发送/接受fileId等字段public static final int SELF = 3; //建立连接后,web端发送client其sessionIdpublic static final int ERR_SESSION = 4; //提示session不存在或已close public static final int HEART_BEAT = 100; //心跳包
}
- 定义web与客户端通信数据格式
@Data
public class MsgData {private int type; //对应 MsgTypeprivate String sessionId; //SELF 对应自身sessionId; REQ 对应pc端sessionId;private String fileId; //建立连接后,向pc端发送fileId等字段
web端websocket实现
创建spring-boot项目,添加web\websocket相关依赖
使用maven引入websocket依赖;
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
配置websocket和访问路径的映射关系
@Configuration //配置websocket和访问路径的映射关系
@EnableWebSocket // 全局开启WebSocket支持
public class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(new WebSocketServer(), "/websocket").setAllowedOrigins("*");}
}
web端对客户端数据的管理
- 定义web管理session的数据结构
@Data
public class SessionData {private int sessionType; // 1 master(app). 0 pcprivate String fileId; //pc会话IDprivate WebSocketSession session;private String sessionId;//虽然可以通过session.getId()获取到sessionId,但session关闭后,读取就会报错
web端对session的管理逻辑
-
新创建的连接添加到链表上,web向客户端发送SELF,告知其对应的sessionId;
-
断开连接时,如果是pc端session直接从链表中删除,如果是app端session,将其他相同fileId的session全部关闭并从链表删除;
-
接收到新消息后,根据消息类型进行分类处理:
- 心跳包,则直接返回;
- REQ app发送的fileId\pc端sessionId等字段,修改sessions上app连接和pc端SessionData内的fileId字段;
并将fileId等字段发送给pc端; - UPDATE 给所有相同fileId的session发送更新信号;
注意: sessions遍历\删除\添加必须添加synchronized,否则ConcurrentModificationException
package com.example.im.ws;import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;/*** @ClassName WebSocketServer* @Description 处理websocket 连接* @Author guchuanhang* @date 2025/1/25 14:01* @Version 1.0**/@Slf4j
public class WebSocketServer extends TextWebSocketHandler {private final Object syncObject = new Object();private final List<SessionData> sessions =new ArrayList<>();@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {log.info("New connection established: " + session.getId());SessionData sessionData = new SessionData(session);synchronized (syncObject) {sessions.add(sessionData);}MsgData msgData = new MsgData();msgData.setType(MsgType.SELF);msgData.setSessionId(session.getId());session.sendMessage(new TextMessage(new Gson().toJson(msgData)));}@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message)throws Exception {String payload = message.getPayload();log.info("handleTextMessage: " + session.getId());log.info("Received message: " + payload);final MsgData msgData = new Gson().fromJson(payload, MsgData.class);//master 发来的需求.switch (msgData.getType()) {case MsgType.HEART_BEAT: {//heart beatbreak;}case MsgType.REQ: {//set master{SessionData sessionData = null;synchronized (syncObject) {final Optional<SessionData> any = sessions.stream().filter(s -> s.getSessionId().equals(session.getId())).findAny();if (any.isPresent()) {sessionData = any.get();}}if (null != sessionData) {//set master.sessionData.setSessionType(ClientType.MASTER);sessionData.setFileId(msgData.getFileId());}}//set slave{SessionData sessionData = null;synchronized (syncObject) {final Optional<SessionData> any = sessions.stream().filter(s -> s.getSessionId().equals(msgData.getSessionId())).findAny();if (any.isPresent()) {sessionData = any.get();}}if (null != sessionData) {sessionData.setSessionType(ClientType.SALVER);sessionData.setFileId(msgData.getFileId());MsgData msgData1 = new MsgData();msgData1.setType(MsgType.REQ);msgData1.setFileId(msgData.getFileId());sessionData.getSession().sendMessage(new TextMessage(new Gson().toJson(msgData1)));} else {//pc session error.MsgData msgData1 = new MsgData();msgData1.setType(MsgType.ERR_SESSION);session.sendMessage(new TextMessage(new Gson().toJson(msgData1)));}}break;}case MsgType.UPDATE: {//slfSessionData sessionData = null;synchronized (syncObject) {final Optional<SessionData> any = sessions.stream().filter(s -> s.getSessionId().equals(session.getId())).findAny();if (any.isPresent()) {sessionData = any.get();}}if (null != sessionData) {final String fileId = sessionData.getFileId();List<SessionData> collect;synchronized (syncObject) {collect =sessions.stream().filter(s -> (null != s.getFileId() && s.getFileId().equals(fileId)) || (null == s.getSession() || !s.getSession().isOpen())).collect(Collectors.toList());}if (collect.isEmpty()) {return;}List<SessionData> errList = new ArrayList<>();for (SessionData s : collect) {if (null == s.getSession() || !s.getSession().isOpen()) {errList.add(s);continue;}//不需要给自己发送了if (s.getSessionId().equals(session.getId())) {continue;}MsgData msgData1 = new MsgData();msgData1.setType(MsgType.UPDATE);try {s.getSession().sendMessage(new TextMessage(new Gson().toJson(msgData1)));} catch (Exception e) {e.printStackTrace();errList.add(s);}}synchronized (syncObject) {sessions.removeAll(errList);}}break;}}}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {log.info("Connection closed: " + session.getId() + ", Status: " + status);SessionData sessionData = null;synchronized (syncObject) {Optional<SessionData> any = sessions.stream().filter(s -> s.getSessionId().equals(session.getId())).findAny();if (any.isPresent()) {sessionData = any.get();}}if (null == sessionData) {return;}final String fileId = sessionData.getFileId();//slave just ignore and delete.if (ClientType.SALVER == sessionData.getSessionType()) {sessions.remove(sessionData);return;}if (ClientType.MASTER == sessionData.getSessionType()) {List<SessionData> collect;synchronized (syncObject) {collect =sessions.stream().filter(s ->(null != s.getFileId()&& s.getFileId().equals(fileId)) ||(null == s.getSession() || !s.getSession().isOpen())).collect(Collectors.toList());}if (collect.isEmpty()) {return;}for (SessionData s : collect) {final WebSocketSession session1 = s.getSession();if (null == session1 || !session1.isOpen()) {continue;}session1.close();}synchronized (syncObject) {sessions.removeAll(collect);}}}
}
pc端实现
- 页面创建时创建websocket,销毁时关闭websocket
- 根据和服务器约定的消息格式 在websocket回调函数onmessage接受数据类型进行二维码生成\文件列表查询等操作
- 添加心跳机制,让websocket更健壮
fileId是一个key,通过fileId可以查询最新的数据. pc端接受到刷新信号后,请求获取最新数据; pc端更新数据后,发送数据已更新信号.
<template><div v-if="fileId"><div>{{ fileId }}</div><el-button @click="updateData" type="primary">更新数据</el-button><div>发送给服务端更新信号时间: {{ sndUpdateSignalTime }}</div><div>收到服务端更新信号时间: {{ rcvUpdateSignalTime }}</div><div>心跳最新时间: {{ heartBeatSignalTime }}</div><div>服务器返回最新内容: {{ serverContent }}</div></div><div v-else-if="sessionId"><div>sessionId:{{ sessionId }}</div><img width="200px" height="200px" :src="qrCode" alt="QR Code"/></div></template><script>import QRCode from "qrcode";export default {name: "HelloWorld",data() {return {wsuri: "ws://192.168.0.110:7890/websocket",ws: null,sessionId: '',qrCode: null,fileId: '',rcvUpdateSignalTime: '',sndUpdateSignalTime: '',heartBeatSignalTime: '',serverContent: '',heartbeatInterval: null,heartbeatIntervalTime: 3000, // 心跳间隔时间,单位为毫秒}},created() {//页面打开时,初始化WebSocket连接this.initWebSocket()},beforeDestroy() {// 页面销毁时,关闭WebSocket连接this.stopHeartbeat()this.fileId = ''try {this.ws.close()} catch (e) {}this.ws = null;this.sessionId = ''},methods: {// pc端更新附件数据后,向服务器端发送更新信号updateData() {console.error('snd update signal')this.ws.send(JSON.stringify({type: 0}))//格式化为 yyyy-MM-dd HH:mm:ssthis.sndUpdateSignalTime = new Date().toLocaleTimeString()this.resetHeartbeat();},async generateQRCode() {try {this.qrCode = await QRCode.toDataURL(this.sessionId);} catch (error) {console.error('生成二维码时出错:', error);}},// 周期性发送心跳包startHeartbeat() {this.heartbeatInterval = setInterval(() => {if (this.ws && this.ws.readyState === WebSocket.OPEN) {this.ws.send(JSON.stringify({type: 100}))this.heartBeatSignalTime = new Date().toLocaleTimeString()console.error('snd heartbeat signal')} else {this.stopHeartbeat();}}, this.heartbeatIntervalTime);},//在发送或接受数据后,重置下一次发送心跳包的时间resetHeartbeat() {clearInterval(this.heartbeatInterval);this.startHeartbeat();},// 停止发送心跳包stopHeartbeat() {clearInterval(this.heartbeatInterval);},initWebSocket() {let that = this;this.ws = new WebSocket(this.wsuri);this.ws.onopen = () => {this.startHeartbeat();};// 接收后端消息this.ws.onmessage = function (event) {console.error('RECV:' + event.data)that.serverContent = event.data;let parse = JSON.parse(event.data);that.resetHeartbeat();switch (parse.type) {case 0: {console.error('update')that.rcvUpdateSignalTime = new Date().toLocaleTimeString()//TODO. 请求最新数据break;}case 1: { //fileId list. 接受数据,进行路径跳转console.error('REQ:' + event.data)that.fileId = parse.fileId;//记录并请求最新数据break;}case 3: {that.sessionId = parse.sessionId;that.generateQRCode();break;}}};// 关闭连接时调用this.ws.onclose = function (event) {alert('连接已关闭');that.stopHeartbeat()// 强制刷新页面(created 会调用)location.reload(true)};}}
}
</script><style scoped></style>
OkHttp3建立websocket连接
- 使用okhttp3建立websocket连接,监听onMessage根据消息类型进行不同的处理;
- 使用handler 管理心跳包
扫码后, 如果已经建立连接了
package com.example.im.ws;import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.widget.EditText;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;import com.example.im.R;
import com.google.gson.Gson;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;public class HelloActivity extends AppCompatActivity {public static final int MSG_HEART = 0x123;public static final int MSG_INTERVAL = 3000;private WebSocket webSocket;public static final String URL = "ws://192.168.0.110:7890/websocket";private TextView msgView;private List<String> sessionIds = new ArrayList<>();Handler mHandler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);if (MSG_HEART == msg.what) {MsgData msgData = new MsgData();msgData.setType(MsgType.HEART_BEAT);webSocket.send(new Gson().toJson(msgData));msgView.append(getNowDate() + ":发送消息 heart beat\n");mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HEART), MSG_INTERVAL);}}};@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);msgView = findViewById(R.id.tv_msg);findViewById(R.id.btn_scan).setOnClickListener(v -> {scanQRCode();});findViewById(R.id.btn_update).setOnClickListener(v -> {MsgData msgData = new MsgData();msgData.setType(MsgType.UPDATE);webSocket.send(new Gson().toJson(msgData));});}@Overrideprotected void onDestroy() {mHandler.removeCallbacksAndMessages(null);super.onDestroy();}private void scanQRCode() {IntentIntegrator integrator = new IntentIntegrator(this);integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE);integrator.setPrompt("提示");integrator.setCameraId(0); // 使用后置摄像头integrator.setBeepEnabled(false);integrator.setBarcodeImageEnabled(true);integrator.initiateScan();}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);if (result != null && !TextUtils.isEmpty(result.getContents())) {String sessionId = result.getContents();if (sessionIds.contains(sessionId)) {return;}sessionIds.add(sessionId);//startif (null == webSocket) {OkHttpClient client = new OkHttpClient();Request request = new Request.Builder().url(URL).build();webSocket = client.newWebSocket(request, new MyWebSocketListener());} else {//这样可以实现扫多个pc端. 同时与多个pc端通信MsgData msgData = new MsgData();msgData.setSessionId(sessionId);msgData.setType(MsgType.REQ);msgData.setFileId("123");webSocket.send(new Gson().toJson(msgData));}}super.onActivityResult(requestCode, resultCode, data);}private String getNowDate() {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.getDefault());return simpleDateFormat.format(new java.util.Date());}private class MyWebSocketListener extends WebSocketListener {@Overridepublic void onOpen(WebSocket webSocket, okhttp3.Response response) {// 连接成功msgView.append(getNowDate() + ":连接成功\n");MsgData msgData = new MsgData();msgData.setSessionId(sessionIds.get(sessionIds.size() - 1));msgData.setType(MsgType.REQ);msgData.setFileId("123");webSocket.send(new Gson().toJson(msgData));mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HEART), MSG_INTERVAL);}@Overridepublic void onMessage(WebSocket webSocket, String text) {msgView.append(getNowDate() + ":接受消息" + text + "\n");mHandler.removeMessages(MSG_HEART);mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HEART), MSG_INTERVAL);}@Overridepublic void onFailure(WebSocket webSocket, Throwable t, okhttp3.Response response) {// 连接失败msgView.append(getNowDate() + ":失败" + t.getMessage() + "\n");}}
}
2.0版本
上面的实现确实简单.下面结合实际的系统架构进行适配一下.
spring-boot放到nginx后面
nginx常用来进行负载均衡\防火墙\反向代理等等,这种情况比较常见.
map $http_upgrade $connection_upgrade { default upgrade; '' close; } server {listen 7777;server_name localhost;location / {proxy_pass http://127.0.0.1:7890;proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }}
}
设置Upgrade\Connection请求头,将访问地址修改为nginx的地址,即可实现nginx代理到spring-boot.
spring-boot 放到gateway后面
也就是所谓的spring-cloud 微服务架构.
gateway添加ws协议的路由
# IM- id: imuri: ws://localhost:7890predicates:- Path=/im/**filters:- StripPrefix=1
访问gateway代理之后的地址,即可实现nginx代理到spring-boot.
spring-boot 放到nginx gateway后面
将前面两者进行结合, nginx保证可以代理到gateway, gateway再路由到spring-boot.
ws升级为wss
网上的做法是, 给gateway\spring-boot都配置证书.
简单才能高效,既然gateway有防火墙验证证书等功能,应用不需要管理才对. nginx要屏蔽这种差异.
配置nginx 直接将wss的请求重写为ws.
nginx重写协议
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server
{listen 443 ssl http2;server_name #SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则ssl on;ssl_certificate ssl_certificate_key add_header Strict-Transport-Security "max-age=31536000";error_page 497 https://$host$request_uri;location /im/ { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_pass http://127.0.0.1:18080/im/;rewrite ^(.*)wss://(.*)$ $1ws://$2 permanent;}
}
这样 wss://域名/im/websocket就可以进行访问了.
其他
源码下载地址: https://gitee.com/guchuanhang/imapplication.git
springboot打包
- 注释掉skip
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.example.im.ImApplication</mainClass><!-- 注释掉,否则不能打包-->
<!-- <skip>true</skip>--></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin>
- springboot日志
spring-boot 默认支持logback
@Slf4j
public class WebSocketServer extends TextWebSocketHandler {log.info("New connection established: " + session.getId());
- bootstrap.yml
bootstrap.yml 是 spring-cloud 配置文件.
application.yml applicaition.properties 是 spring-boot 的配置文件.
- wss测试工具 wscat
npm install -g wscat # 安装方式wscat -c wss://www.baidu.com/im/websocket
相关文章:

websocket实现
由于安卓资源管理器展示的路径不尽相同,各种软件保存文件的位置也不一定一样.对于普通用户上传文件时,查找文件可能是一个麻烦的事情.后来想到了一个办法,使用pc端进行辅助上传. 文章目录 实现思路1.0 实现定义web与客户端通信数据类型和数据格式web端websocket实现web端对客户…...

unity学习20:time相关基础 Time.time 和 Time.deltaTime
目录 1 unity里的几种基本时间 1.1 time 相关测试脚本 1.2 游戏开始到现在所用的时间 Time.time 1.3 时间缩放值 Time.timeScale 1.4 固定时间间隔 Time.fixedDeltaTime 1.5 两次响应时间之间的间隔:Time.deltaTime 1.6 对应测试代码 1.7 需要关注的2个基本…...

【C++】特殊类设计、单例模式与类型转换
目录 一、设计一个类不能被拷贝 (一)C98 (二)C11 二、设计一个类只能在堆上创建对象 (一)将构造函数私有化,对外提供接口 (二)将析构函数私有化 三、设计一个类只…...

scratch七彩六边形 2024年12月scratch三级真题 中国电子学会 图形化编程 scratch三级真题和答案解析
目录 scratch七彩六边形 一、题目要求 1、准备工作 2、功能实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、…...

代码随想录刷题day16|(哈希表篇)349.两个数组的交集
目录 一、哈希表理论基础 二、集合set在哈希法中的应用 三、相关算法题目 四、相关知识点 1.set集合特点和常用方法 1.1 set集合概述 1.2 set集合特点 1.3 常用方法 2.set集合转换成数组 法1:另新建一个数组 法2:将结果集合转为数组 ▲ 3.数组…...

Synology 群辉NAS安装(6)安装mssql
Synology 群辉NAS安装(6)安装mssql 写在前面mssql 2019:成功安装说明,这个最终成功了 mssql 2022没有成功1. pull image2.启动mssql docker container 远程连接 写在前面 mssq是一个重要节点。 这是因为我对mysql没有一丝好感。虽然接触了许…...

2025年美赛B题-结合Logistic阻滞增长模型和SIR传染病模型研究旅游可持续性-成品论文
模型设计思路与创新点: 建模的时候应该先确定我们需要建立什么类的模型?优化类还是统计类?这个题需要大量的数据分析,因此我们可以建立一个统计学模型。 统计学建模思路:观察规律,建立模型,参…...

Hook 函数
什么是hook函数? 在计算机编程中,hook函数是指在特定的事件发生时被调用的函数,用于在事件发生前或后进行一些特定的操作。通常,hook函数作为回调函数被注册到事件处理器中,当事件发生时,事件处理器会自动…...

蓝桥杯模拟算法:蛇形方阵
P5731 【深基5.习6】蛇形方阵 - 洛谷 | 计算机科学教育新生态 我们只要定义两个方向向量数组,这种问题就可以迎刃而解了 比如我们是4的话,我们从左向右开始存,1,2,3,4 到5的时候y就大于4了就是越界了&…...

DeepSeek-R1解读:纯强化学习,模型推理能力提升的新范式?
DeepSeek-R1解读:纯强化学习,模型推理能力提升的新范式? 1. Impressive Points2. 纯强化学习,LLM推理能力提升新范式?2.1 DeepSeek-R1-Zero2.2 DeepSeek-R1 3. 端侧模型能力提升:蒸馏>强化学习 1. Impre…...

深度解析:基于Vue 3的教育管理系统架构设计与优化实践
一、项目架构分析 1. 技术栈全景 项目采用 Vue 3 TypeScript Tailwind CSS 技术组合,体现了现代前端开发的三大趋势: 响应式编程:通过Vue 3的Composition API实现细粒度响应 类型安全:约60%的组件采用TypeScript编写 原子化…...

【PyTorch】3.张量类型转换
个人主页:Icomi 在深度学习蓬勃发展的当下,PyTorch 是不可或缺的工具。它作为强大的深度学习框架,为构建和训练神经网络提供了高效且灵活的平台。神经网络作为人工智能的核心技术,能够处理复杂的数据模式。通过 PyTorch࿰…...

Spring Boot整合JavaMail实现邮件发送
一. 发送邮件原理 发件人【设置授权码】 - SMTP协议【Simple Mail TransferProtocol - 是一种提供可靠且有效的电子邮件传输的协议】 - 收件人 二. 获取授权码 开通POP3/SMTP,获取授权码 授权码是QQ邮箱推出的,用于登录第三方客户端的专用密码。适用…...

字节跳动发布UI-TARS,超越GPT-4o和Claude,能接管电脑完成复杂任务
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...

数据的秘密:如何用大数据分析挖掘商业价值
数据的秘密:如何用大数据分析挖掘商业价值 在这个数据爆炸的时代,我们每天都在产生、存储和处理着海量的数据。然而,仅仅拥有数据并不等于拥有价值。就像拥有一座金矿,不开采和提炼,最终只是一堆毫无用处的石头。如何…...

OAuth1和OAuth2授权协议
OAuth 1 授权协议 1. 概述 OAuth1 是 OAuth 标准的第一个正式版本,它通过 签名和令牌 的方式,实现用户授权第三方访问其资源的功能。在 OAuth1 中,安全性依赖于签名机制,无需传递用户密码。 2. 核心特性 使用 签名(…...

AI学习(vscode+deepseek+cline)
1、网页生成不成功时,直接根据提示让模型替你解决问题 2、http://localhost:3000 拒绝链接时,cmd输入命令InetMgr,网站右键新建-配置你的网页代码物理地址,这里我还输入本机登录名及密码了,并把端口地址由默认80修改为…...

04-机器学习-网页数据抓取
网络爬取(Web Scraping)深度指南 1. 网络爬取全流程设计 一个完整的网络爬取项目通常包含以下步骤: 目标分析: 明确需求:需要哪些数据(如商品价格、评论、图片)?网站结构分析&…...

计网week1+2
计网 一.概念 1.什么是Internet 节点:主机及其运行的应用程序、路由器、交换机 边:通信链路,接入网链路主机连接到互联网的链路,光纤、网输电缆 协议:对等层的实体之间通信要遵守的标准,规定了语法、语义…...

重定向与缓冲区
4种重定向 我们有如下的代码: #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h>#define FILE_NAME "log.txt"int main() {close(1)…...

练习题 - Django 4.x File 文件上传使用示例和配置方法
在现代的 web 应用开发中,文件上传是一个常见的功能,无论是用户上传头像、上传文档,还是其他类型的文件,处理文件上传都是开发者必须掌握的技能之一。Django 作为一个流行的 Python web 框架,提供了便捷的文件上传功能和配置方法。学习如何在 Django 中实现文件上传,不仅…...

[VSCode] vscode下载安装及安装中文插件详解(附下载链接)
VSCode 是一款由微软开发且跨平台的免费源代码编辑器;该软件支持语法高亮、代码自动补全、代码重构、查看定义功能,并且内置了命令行工具和Git版本控制系统。 下载链接:https://pan.quark.cn/s/3a90aef4b645 提取码:NFy5 通过上面…...

JVM常见知识点
在《深入理解Java虚拟机》一书中,介绍了JVM的相关特性。 1、JVM的内存区域划分 在真实的操作系统中,对于地址空间进行了分区域的设计,由于JVM是仿照真实的机器进行设计的,那么也进行了分区域的设计。核心区域有四个,…...

深入探索 Vue 3 Markdown 编辑器:高级功能与实现
目录 1. 为什么选择 Markdown 编辑器?2. 选择合适的 Markdown 编辑器3. 安装与基本配置安装 配置 Markdown 编辑器代码说明 4. 高级功能实现4.1 实时预览与双向绑定4.2 插入图片和图像上传安装图像上传插件配置图像上传插件 4.3 数学公式支持安装 KaTeX配置 KaTeX 插…...

vscode无法格式化go代码的问题
CTRLshiftp 点击Go:Install/Update Tools 点击全选,OK!...

《Java程序设计》课程考核试卷
一、单项选择题(本大题共10个小题,每小题2分,共20分) 1.下列用来编译Java源文件为字节码文件的工具是( )。 A.java B.javadoc C.jar D.javac 2…...
one-hot (独热编码)
一、目的 假设我们现在需要对猫、 狗、 人这三个类别进行分类。 若以 0 代表猫, 以 1 代表狗, 以 2 代表人,会发现那么猫和狗之间距离为 1, 狗和人之间距离为 1, 而猫和人之间距离为 2。 假设真实标签是猫࿰…...

寒假1.23
题解 web:[极客大挑战 2019]Secret File(文件包含漏洞) 打开链接是一个普通的文字界面 查看一下源代码 发现一个链接,点进去看看 再点一次看看,没什么用 仔细看,有一个问题,当点击./action.ph…...

unity 粒子系统设置触发
1、勾选Triggers选项 2、将作为触发器的物体拉入队列当中,物体上必须挂载collider 3、将想要触发的方式(Inide、Outside、Enter和Exit)选择为”Callback“,其他默认为”Ignore“ 4、Collider Query Mode 设置为All:…...

【C++】类和对象(五)
1、初始化列表 作用:C提供了初始化列表语法,用来初始化属性。 语法: 构造函数():属性1(值1),属性2(值2)...{}示例: #include<i…...