首页
苏兮影视
随笔记
壁纸
更多
直播
时光轴
友联
关于
统计
Search
1
v2ray节点搭建
1,074 阅读
2
软件添加id功能按钮
1,029 阅读
3
QQ扫码无法登录的解决方案
990 阅读
4
网易云音乐歌单ID获取教程
922 阅读
5
js逆向xhs
792 阅读
谈天说地
建站源码
经验教程
资源分享
动漫美图
登录
Search
标签搜索
java
rust
flutter
esp32c3
springboot
安卓
linux
vue
dart
设计模式
docker
ssh
joe
快捷键
git
fish shell
maven
redis
netty
groovy
尽意
累计撰写
116
篇文章
累计收到
41
条评论
首页
栏目
谈天说地
建站源码
经验教程
资源分享
动漫美图
页面
苏兮影视
随笔记
壁纸
直播
时光轴
友联
关于
统计
搜索到
116
篇与
的结果
Vue 3 和 WebRTC 实现点对点聊天应用
使用 Vue 3 和 WebRTC 来实现一个简单的点对点聊天应用,服务端使用 WebSocket (ws) 来建立信令服务器,用于连接和交换 WebRTC 所需的 SDP 和 ICE 候选信息。1. 项目初始化首先,我们创建一个基于 Vue 3 的项目:npm init vue@latest webrtc-chat cd webrtc-chat npm install然后,安装 WebSocket 依赖:npm install ws2. 创建 WebSocket 信令服务器我们使用 Node.js 和 ws 模块来创建信令服务器:// server.js const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { ws.on('message', (message) => { // 将消息广播给其他客户端 wss.clients.forEach((client) => { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(message); } }); }); // 向新连接的客户端发送欢迎消息 ws.send(JSON.stringify({ type: 'welcome', message: 'Connected to WebSocket server' })); }); console.log('WebSocket server is running on ws://localhost:8080');运行服务器:node server.js3. 创建 Vue 3 应用在 src 文件夹中创建 components/Chat.vue 组件。<template> <div> <h2>点对点聊天</h2> <textarea v-model="message" placeholder="输入消息..."></textarea> <button @click="sendMessage">发送</button> <button @click="createOffer">发送 Offer</button> <div v-for="(msg, index) in messages" :key="index">{{ msg }}</div> </div> </template> <script> export default { data() { return { message: '', messages: [], peerConnection: null, dataChannel: null, ws: null }; }, mounted() { this.initializeWebSocket(); // 初始化 WebSocket this.initializePeerConnection(); // 初始化 PeerConnection }, methods: { // 初始化 WebSocket 连接 initializeWebSocket() { this.ws = new WebSocket('ws://localhost:8080'); // 接收来自信令服务器的消息 this.ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'offer') { this.handleOffer(data); // 处理接收到的 Offer } else if (data.type === 'answer') { this.handleAnswer(data); // 处理接收到的 Answer } else if (data.type === 'candidate') { this.addIceCandidate(data); // 添加接收到的 ICE 候选 } }; }, // 初始化 PeerConnection 和数据通道 initializePeerConnection() { this.peerConnection = new RTCPeerConnection(); // 监听 ICE 候选事件 this.peerConnection.onicecandidate = (event) => { if (event.candidate) { this.ws.send(JSON.stringify({ type: 'candidate', candidate: event.candidate })); // 将 ICE 候选发送到信令服务器 } }; // 创建数据通道 this.dataChannel = this.peerConnection.createDataChannel('chat'); this.dataChannel.onmessage = (event) => { this.messages.push(`Peer: ${event.data}`); // 接收消息并更新聊天记录 }; // 监听远程数据通道 this.peerConnection.ondatachannel = (event) => { this.dataChannel = event.channel; this.dataChannel.onmessage = (event) => { this.messages.push(`Peer: ${event.data}`); }; }; }, // 创建并发送 Offer async createOffer() { const offer = await this.peerConnection.createOffer(); await this.peerConnection.setLocalDescription(offer); this.ws.send(JSON.stringify({ type: 'offer', sdp: offer })); // 发送 Offer }, // 处理接收到的 Offer async handleOffer(data) { await this.peerConnection.setRemoteDescription(new RTCSessionDescription(data.sdp)); const answer = await this.peerConnection.createAnswer(); await this.peerConnection.setLocalDescription(answer); this.ws.send(JSON.stringify({ type: 'answer', sdp: answer })); // 发送 Answer }, // 处理接收到的 Answer async handleAnswer(data) { await this.peerConnection.setRemoteDescription(new RTCSessionDescription(data.sdp)); }, // 添加接收到的 ICE 候选 async addIceCandidate(data) { if (data.candidate) { await this.peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate)); } }, // 发送消息 sendMessage() { if (this.dataChannel && this.message) { this.dataChannel.send(this.message); // 通过数据通道发送消息 this.messages.push(`Me: ${this.message}`); // 更新本地聊天记录 this.message = ''; // 清空输入框 } } } }; </script> <style> textarea { width: 100%; height: 100px; margin-bottom: 10px; } button { display: block; margin-bottom: 10px; } </style>4. 启动应用运行以下命令以启动 Vue 应用:npm run dev打开浏览器开两个窗口访问 http://localhost:3000,点击发送offer即可进行实时的点对点聊天。
2024年11月15日
298 阅读
0 评论
3 点赞
Vue 3 中使用 Tailwind CSS
1. 项目初始化与安装 Tailwind CSS在 Vue 3 项目中使用 Tailwind CSS 需要先进行初始化配置。这里以 Vue CLI 或 Vite 为例,分别介绍两种常见的初始化方式。使用 Vue CLI创建 Vue 3 项目:vue create my-project cd my-project安装 Tailwind CSS:vue add tailwind使用 Vite创建 Vue 3 项目:npm create vite@latest my-project --template vue cd my-project安装 Tailwind CSS:npm install -D tailwindcss postcss autoprefixer npx tailwindcss init配置 tailwind.config.js 和 postcss.config.js(Vite 会自动生成这些文件,确保使用默认配置即可):tailwind.config.js:module.exports = { content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], theme: { extend: {}, }, plugins: [], }postcss.config.js:module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, }2. 在 Vue 组件中使用 Tailwind CSSTailwind 的类名可以直接在 Vue 组件中使用,只需在模板中添加类名即可。比如,使用 Tailwind 设置按钮样式:<template> <button class="bg-blue-500 text-white p-2 rounded">Click Me</button> </template>你可以根据项目需求自定义类名,也可以使用 Tailwind 提供的响应式和伪类变体。例如,设置一个响应式的布局:<template> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> <div class="bg-gray-200 p-4">Item 1</div> <div class="bg-gray-200 p-4">Item 2</div> <div class="bg-gray-200 p-4">Item 3</div> </div> </template>3. 配置 Tailwind 的自定义主题Tailwind 提供了丰富的自定义配置选项,你可以在 tailwind.config.js 中修改颜色、字体、间距等。例如,修改默认的颜色主题:module.exports = { theme: { extend: { colors: { primary: '#FF6347', // 设置自定义颜色 }, }, }, }在 Vue 组件中使用自定义颜色:<template> <button class="bg-primary text-white p-2 rounded">Click Me</button> </template>4. 使用 Tailwind 的插件功能Tailwind 有很多官方插件,可以为你的项目带来额外的功能。例如,@tailwindcss/forms 插件可以改善表单元素的样式。安装插件:npm install @tailwindcss/forms在 tailwind.config.js 中引入插件:module.exports = { plugins: [ require('@tailwindcss/forms'), ], }使用表单插件后,你的表单元素将自动应用更好的样式:<template> <form> <input type="text" class="form-input mt-1 block w-full" /> <button class="bg-primary text-white p-2 rounded">Submit</button> </form> </template>5. 使用 Tailwind 的 JIT 模式(即时编译模式)Tailwind CSS 默认开启了 JIT 模式(在 2.x 版本后),这意味着只有在使用到的 CSS 类才会被编译到最终的 CSS 文件中,极大地减少了打包后的文件大小。若要显式开启 JIT 模式,可以在 tailwind.config.js 中进行设置:module.exports = { mode: 'jit', content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], theme: { extend: {}, }, }6. 优化 Tailwind 打包结果在生产环境中,使用 Tailwind CSS 可能会导致打包后的文件过大。为了减少文件大小,可以使用 purge 或 content 配置来确保只编译实际使用到的 CSS 类。在 tailwind.config.js 中启用内容扫描:module.exports = { content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], }这样,Tailwind 只会编译扫描到的文件中的 CSS 类,从而减少最终的 CSS 文件大小。7. 组合 Vue 3 的响应式特性与 TailwindTailwind 支持响应式设计,可以结合 Vue 3 的 v-bind 和计算属性等响应式特性来动态更新类名。通过 v-bind:class 可以动态绑定类。<template> <div :class="buttonClass"> <button>Click Me</button> </div> </template> <script setup> import { ref } from 'vue' const isPrimary = ref(true) const buttonClass = computed(() => ({ 'bg-blue-500 text-white': isPrimary.value, 'bg-gray-500 text-black': !isPrimary.value, })) </script>8. 常见问题解决CSS 样式不生效:如果发现 Tailwind 样式没有生效,检查是否正确配置了 tailwind.config.js,并确保 content 配置中包含了你的组件路径。文件过大:开启 JIT 模式和内容扫描,以确保只编译用到的类。9. 资源与参考Tailwind CSS 官方文档Vue 3 官方文档Tailwind CSS 与 Vue 3 集成教程
2024年11月15日
363 阅读
0 评论
3 点赞
v2ray节点搭建
快速开始:https://233boy.com/v2ray/v2ray-script/详细说明:https://bulianglin.com/archives/guide.html
2024年11月06日
1,074 阅读
0 评论
2 点赞
2024-11-06
esp32c3自动配网
在 esp32-c3 rust使用docker编译 新建项目的基础上,引入必要的依赖Cargo.toml[dependencies] log = { version = "0.4", default-features = false } esp-idf-svc = { version = "0.49", default-features = false } # 添加http的依赖 anyhow = "=1.0.86" embedded-svc = "=0.28.0" # json处理 serde = { version = "1.0", features = ["derive"] } # 序列化,支持多种,xml,json等 serde_json = "1.0" # 专门处理json格式 新建一个跟main.rs同级的配网文件,auto_set_wifi.rs然后在main.rs文件中引用mod auto_set_wifi;编写自动配网rust代码 // 引入配网页面的html文件 static INDEX_HTML: &str = include_str!("../index.html"); // 设置nvs存储的参数 static WIFI_INFO_KEY: &str = "WIFI_INFO"; static NAME_SPACE: &str = "ESP_WIFI"; // 默认ap模式下的配置信息 static AP_WIFO_INFO: WifiInfo<'static> = WifiInfo { ssid: "esp32_c3_wifi", password: "00000000", }; #[derive(Debug, Deserialize, Serialize)] struct WifiInfo<'a> { ssid: &'a str, password: &'a str, } pub fn start_auto_wifi() -> anyhow::Result<()> { // 自动配网,需要先开启ap模式,让手机连上esp32的热点 let peripherals = Peripherals::take()?; let sysloop = EspEventLoop::take()?; let nvs = EspDefaultNvsPartition::take()?; let mut wifi = BlockingWifi::wrap( EspWifi::new(peripherals.modem, sysloop.clone(), Some(nvs.clone()))?, sysloop.clone(), )?; wifi.set_configuration(&esp_idf_svc::wifi::Configuration::AccessPoint( AccessPointConfiguration { ssid: AP_WIFO_INFO.ssid.try_into().unwrap(), password: AP_WIFO_INFO.password.try_into().unwrap(), auth_method: esp_idf_svc::wifi::AuthMethod::WPA2Personal, ..Default::default() }, ))?; // 由于wifi,跟nvs在后面的函数也需要被调用,所有权不能丢弃,需要用安全的智能指针进行接管 let wifi = Arc::new(Mutex::new(wifi)); let nvs = Arc::new(Mutex::new(EspNvs::new(nvs, NAME_SPACE, true)?)); let temp_wifi = wifi.clone(); let temp_nvs = nvs.clone(); let mut buf = [0;128]; log::warn!("正在查找是否存在配网信息..."); if let Err(_) = nvs.lock().unwrap().get_raw(WIFI_INFO_KEY, &mut buf) { log::info!("不存在配网信息,开启ap模式"); let mut temp_wifi = temp_wifi.lock().unwrap(); temp_wifi.start()?; log::info!("开启wifi"); temp_wifi.wait_netif_up()?; log::info!("等待底层网络分配"); log::info!("WiFi信息为:{:?}", temp_wifi.wifi().ap_netif().get_ip_info()); } else { log::info!("存在配网信息,开启混合模式"); let _ = std::thread::spawn(|| start_muxt_mode(temp_wifi, temp_nvs)); } // 配网页面路由 let temp_nvs = nvs.clone(); let mut server = EspHttpServer::new(&ServerConfig::default())?; server.fn_handler("/", esp_idf_svc::http::Method::Get, |request| { request .into_ok_response() .unwrap() .write_all(INDEX_HTML.as_bytes()) })?; // 提交配网的表单信息 server.fn_handler( "/wifi", esp_idf_svc::http::Method::Post, move |mut request| { let len = request.content_len().unwrap() as usize; let mut buf = vec![0; len]; request.read_exact(&mut buf).unwrap(); let temp_nvs = temp_nvs.clone(); let mut response = request.into_ok_response().unwrap(); let raw = temp_nvs.lock().unwrap().set_raw(WIFI_INFO_KEY, &buf); if let true = raw.unwrap_or(false) { response.write_all(b"{\"msg\":\"success\"}").unwrap(); } else { response.write_all(b"{\"msg\":\"faiture\"}").unwrap(); } response.flush().unwrap(); Ok(()) }, )?; // 查看nvs里保存的配网信息 let temp_nvs = nvs.clone(); server.fn_handler("/nvs", esp_idf_svc::http::Method::Get, move |request| { let temp_nvs = temp_nvs.clone(); let mut buf = [0; 128]; let mut response = request.into_ok_response().unwrap(); if let Option::None = temp_nvs .lock() .unwrap() .get_raw(WIFI_INFO_KEY, &mut buf) .unwrap_or(Option::None) { response.write_all(b"{\"msg\":\"faiure\"}").unwrap(); } else { let end = buf.iter().position(|&x| x == 0).unwrap_or(buf.len()); let res = String::from_utf8_lossy(&buf[..end]); let wifi_info = serde_json::from_slice::<WifiInfo>(&buf[..end]).unwrap(); log::error!("WiFi信息为:{:?}", wifi_info); response.write_all(res.as_bytes()).unwrap(); } response.flush() })?; // 开启混合模式路由 let temp_wifi = wifi.clone(); let temp_nvs = nvs.clone(); server.fn_handler("/muxt", esp_idf_svc::http::Method::Get, move |request| { request.into_ok_response().unwrap().write(b"okk").unwrap(); let (temp_wifi, temp_nvs) = (temp_wifi.clone(), temp_nvs.clone()); let _ = std::thread::spawn(|| start_muxt_mode(temp_wifi, temp_nvs)); Ok(()) })?; // 删除nvs的配置信息 let temp_nvs = nvs.clone(); server.fn_handler("/del", esp_idf_svc::http::Method::Get, move |request| { let mut response = request.into_ok_response().unwrap(); let mut temp_nvs = temp_nvs.lock().unwrap(); if let Err(e) = temp_nvs.remove(WIFI_INFO_KEY) { response.write_all(format!("delete faiure,{e}").as_bytes()) }else{ response.write_all(b"delete success") } })?; forget(server); forget(wifi); Ok(()) } pub fn start_muxt_mode( wifi: Arc<Mutex<BlockingWifi<EspWifi>>>, nvs: Arc<Mutex<EspNvs<NvsDefault>>> )-> anyhow::Result<()> { // 启动混合模式 log::info!("延迟500毫秒执行"); FreeRtos::delay_ms(500); log::info!("开始配置"); let mut wifi = wifi.lock().unwrap(); let nvs = nvs.lock().unwrap(); // 重试连接的次数 let mut retry = 5; loop { if retry < 0 { log::error!("尝试剩余{retry}次后,连接失败"); break; } if wifi.is_started()? { if let Err(_) = wifi.stop() { log::error!("断开wifi连接失败,剩余{}次重试",retry); retry-=1; continue; } log::info!("断开wifi"); } let mut buf = [0; 128]; if let Option::None = nvs.get_raw(WIFI_INFO_KEY, &mut buf).unwrap_or(Option::None) { log::error!("获取WiFi信息失败,剩余{}次重试",retry); } let end = buf.iter().position(|&x| x == 0).unwrap_or(buf.len()); let wifi_info = serde_json::from_slice::<WifiInfo>(&buf[..end]).unwrap(); log::warn!("WiFi信息为:{:?}", wifi_info); let client_config = ClientConfiguration { ssid: wifi_info.ssid.try_into().unwrap(), password: wifi_info.password.try_into().unwrap(), ..Default::default() }; let ap_config = AccessPointConfiguration { ssid: AP_WIFO_INFO.ssid.try_into().unwrap(), password: AP_WIFO_INFO.password.try_into().unwrap(), auth_method: esp_idf_svc::wifi::AuthMethod::WPA2Personal, ..Default::default() }; wifi.set_configuration(&esp_idf_svc::wifi::Configuration::Mixed( client_config, ap_config, ))?; log::info!("启动wifi"); wifi.start()?; log::info!("等待网络连接"); if let Err(e) = wifi.connect() { log::error!("连接失败:{e},剩余{}次连接尝试", retry); retry -= 1; continue; } log::info!("等待底层网络驱动处理"); if let Err(e) = wifi.wait_netif_up() { log::error!("等待底层网络驱动处理失败,{e},剩余{}次重试",retry); } break; } log::info!("Ap模式信息为:{:?}", wifi.wifi().ap_netif().get_ip_info()); log::info!("Ip信息为:{:?}", wifi.wifi().sta_netif().get_ip_info()); Ok(()) } 在main.rs中调用fn main() { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 esp_idf_svc::sys::link_patches(); // Bind the log crate to the ESP Logging facilities esp_idf_svc::log::EspLogger::initialize_default(); start_auto_wifi().unwrap(); }配网的html页面代码<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>esp32配网页面</title> <style> body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f4f4f9; } .login-container { width: 300px; padding: 20px; background-color: #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); border-radius: 8px; } .login-container h2 { text-align: center; margin-bottom: 20px; color: #333; } .login-container input { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } .login-container button { width: 100%; padding: 10px; background-color: #4CAF50; border: none; color: #fff; border-radius: 4px; cursor: pointer; font-size: 16px; } .login-container button:hover { background-color: #45a049; } </style> </head> <body> <div class="login-container"> <h2>配网页面</h2> <form id="loginForm"> <input type="text" id="username" placeholder="ssid" required> <input type="password" id="password" placeholder="密码" required> <button type="button" onclick="submitForm()">提交</button> </form> </div> <script> function submitForm() { const ssid = document.getElementById('username').value; const password = document.getElementById('password').value; fetch(window.location.origin + '/wifi', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ssid, password }) }) .then(response => response.json()) .then(data => { // 在此处理响应数据 alert(data.msg); if (data.msg === "success"){ fetch(window.location.origin + '/muxt'); } console.log('成功:', data); }) .catch(error => { console.error('错误:', error); }); } </script> </body> </html>
2024年11月06日
300 阅读
0 评论
3 点赞
配置和使用 CDN 加速网站
在现代网站建设中,为了提高访问速度和用户体验,使用内容分发网络(CDN)成为了一个普遍的选择。本文将带你了解 CDN 配置的基本流程,帮助你为网站实现高效的加速和稳定性。一、什么是 CDN?CDN,全称为内容分发网络(Content Delivery Network),是一种通过将网站内容分发到全球不同的节点来加速用户访问的技术。通过 CDN,当用户请求访问时,系统会将请求路由到离用户最近的服务器节点,减少响应时间并提高加载速度。二、CDN 配置的基本步骤以下是如何将 CDN 集成到你的网站的详细步骤:1. 注册并登录 CDN 平台首先,选择一个适合的 CDN 服务提供商,例如 腾讯云、阿里云、或本地云服务商。注册并登录 CDN 服务控制台。2. 添加加速域名在 CDN 管理平台上,添加你想加速的域名,例如 pan.example.com。CDN 平台会为你生成一个唯一的 CNAME 地址,例如 cdn.exampleprovider.com,这是用于 DNS 解析的。3. 配置源站源站域名:设置为网站服务器的实际域名或 IP 地址,如 pan.example.com 或服务器的公共 IP。回源 Host:通常设置为源站域名 pan.example.com。这确保了回源请求中的 Host 头与原始请求一致。如果有特殊需求,可根据情况调整。4. DNS 解析配置在你的域名服务商的 DNS 控制台中,将 pan.example.com 的 DNS 记录配置为 CNAME,并指向 CDN 提供的 CNAME 地址 cdn.exampleprovider.com。保存配置并等待 DNS 解析生效,这个过程可能需要几分钟到几小时。5. 调整回源设置回源方式:选择通过域名或 IP 进行回源。如果使用 IP,可以避免循环解析问题,但需要保证 IP 的稳定性。缓存策略:根据你的网站内容类型来设置缓存时间,例如静态资源可以长时间缓存,而动态内容应设置较短的缓存时间或不缓存。回源协议:设置为 HTTP 或 HTTPS,根据你网站的安全需求选择合适的协议。6. 检查配置和测试完成配置后,通过浏览器或网络工具访问 pan.example.com,确认访问流量已经通过 CDN 节点加速。同时,可以在 CDN 平台查看访问日志和缓存命中率,并根据需要优化缓存策略。三、常见问题及解决方案1. 加速域名与源站域名相同在某些情况下,你可能想将加速域名(如 pan.example.com)与源站域名设置为相同。这是可行的,但需注意以下几点:DNS 配置:确保加速域名正确解析为 CDN 提供的 CNAME 地址。回源避免循环:CDN 配置时,可以选择使用源站 IP 来避免请求回源至 CDN 本身。2. 回源 Host 的选择回源 Host 设置为源站域名(如 pan.example.com)即可。如果源站有特殊的 Host 验证需求,确保该 Host 设置能够被服务器接受和处理。3. 配置未生效或请求循环检查 DNS 解析是否正确指向 CDN 的 CNAME 地址。确认 CDN 配置中的源站地址无误,确保它不会导致回源请求指向 CDN 节点。四、优化 CDN 的使用分层缓存:根据网站的访问模式和流量来源设置多级缓存策略,以实现更高的缓存命中率。带宽和流量监控:定期检查 CDN 提供商的控制台,查看带宽使用和流量统计,避免超出预算。安全功能:使用 CDN 提供的额外安全功能,如 DDoS 防护和 Web 应用防火墙(WAF),来保护网站免受攻击。
2024年11月06日
198 阅读
0 评论
3 点赞
1
...
6
7
8
...
24