微信小程序 WebSocket 通信 —— 在线聊天
在Node栏目就讲到了Socket通信的内容,使用Node实现Socke通信,还使用两个流行的WebSocket 库,ws 和 socket.io,在小程序中的WebSocket接口和HTML5的WebSocket基本相同,可以实现浏览器与服务器之间的全双工通信。那么本篇就来讲关于微信小程序实现WebSocket通信完成在线聊天。
客户端流程
这里的客户端也就是微信小程序了,同时还需要使用Node搭载一个服务器,WebSocket 是客服端与服务器之间专门建立的一条通道,先来了解一下过程:
通过 wx.connectSocket 来创建 WebSocket 连接,来连接搭载好的Node服务器,连接之后发送数据可以通过 wx.sendSocketMessage ,可以发送数据到达客户端,同时也需要监听接收来自客户端发出的消息事件可以使用 wx.onSocketMessage ,知道这个流程之后,来看一下服务器的搭载流程。
服务器流程
使用Node搭载服务器,WebSocket 服务是建立在HTTP之上的,可以通过引入 http 模块,使用 http.createServer() 创建 HTTP服务器,安装WebSocket库,当然这里可以使用Node中net模块,这里附上可供你参考的篇目:
通过 WebSocket库来创建 WebSocket服务器,同时还需要设置 autoAcceptConnections ,对客服端发送的数据进行一个监控,以及关闭连接的监控,此外还可以进行其他的监控像错误,以及控制台的输入等操作。
图解
用一个图来简单概述一下:
下面来使用WebSokcet通信实现在线聊天,那先来看看需要完成的效果内容:
以上就是接下来要完成的一个场景,先从小程序入手:
1. 聊天界面编写
编写小程序的聊天界面,这里也不再多讲,主要还是讲一下注意点,聊天的数据不可能是一条两条,所以它应该是一个scroll-view滚动的,底部的输入框是固定在底部的,同时要避免底部会把最后的消息数据给遮挡了,聊天数据可以数组保存,然后通过wx:for渲染,是左边的还是右边发出的数据可以通过wx:if来进行判断显示左边还是右边,不破坏原型的DOM结构可以用block标签。
<!--pages/wxchat/wxchat.wxml--><!-- 聊天信息 -->
<view class="content"><scroll-view class="content-info" scroll-y scroll-top="{{scrollTop}}"><view class="content-list"><view class="content-li" wx:for="{{infoList}}" wx:key="id"> <!-- 右边 --><block wx:if="{{item.role === 'self'}}"><view class="right-info"><image class="myhead" src="{{head.self}}"></image><text class="myinfo">{{item.content}}</text></view></block><!-- 左边 --><block wx:else><view class="left-info"><image class="myhead" src="{{head.server}}"></image><text class="myinfo">{{item.content}}</text></view></block></view></view></scroll-view>
</view><!-- 操作区 -->
<view class="foot-input"><input type="text" class="send-input" placeholder="请输入聊天内容..." value="{{message}}" bindinput="handleChange"/><view class="send-btn" bindtap="handleSend">发送</view>
</view>
/* pages/wxchat/wxchat.wxss */
page{height: 100%;
}
.content{height: 100%;display: flex;
}
.content-info .content-list{margin-bottom: 88rpx;
}
/* 信息 */
.content .content-info .content-list .content-li{clear: both;overflow: hidden;
}
.left-info{clear: both;margin: 20rpx 20rpx;
}
.left-info .myhead{float: left;width: 88rpx;height: 88rpx;/* background:#efefef; */
}
.left-info .myinfo{display: block;float: left;background: #75c475;max-width: 490rpx;margin-top: 10rpx;margin-left: 20rpx;padding: 10rpx 18rpx;border-radius: 20rpx;
}
.left-info::after{content:'';clear: both;display: block;height: 0;visibility: hidden;
}
.right-info{clear: both;margin: 10rpx 20rpx;
}
.right-info .myinfo{display: block;float: right;background: #efefef;max-width: 490rpx;margin-top: 10rpx;margin-right: 20rpx;padding: 10rpx 18rpx;border-radius: 20rpx;
}
.right-info .myhead{float: right;width: 88rpx;height: 88rpx;/* background-color: #efefef; */
}
.right-info::after{content:'';clear: both;display: block;height: 0;visibility: hidden;
}/* 底部 */
.foot-input{position: fixed;bottom: 0;border-top: 1px solid #DDDDDD;width: 750rpx;height: 88rpx;line-height: 88rpx;background: #ffffff;
}
.foot-input .send-input{float: left;width: 560rpx;max-width: 600rpx;margin: 16rpx 0 0 20rpx;padding: 0 10rpx;
}
.foot-input .send-btn{float: right;margin: 11rpx 20rpx 0 0;width: 120rpx;height: 66rpx;line-height: 66rpx;text-align: center;background: #16AA51;color: #ffffff;border-radius: 10rpx;
}
2. 小程序创建服务器连接
小程序要接入WebSocketServer服务器,首先要创建服务器连接,可以通过wx.connectSocket() 方法来创建连接,在小程序初始加载的时候就可以进行,同时还可以使用 wx.showToast() 方法提示接入成功与否;
/* pages/wxchat/wxchat.js*/
Page({onLoad:function(){this.createSocketServer();},// 创建服务器连接createSocketServer(){// 服务器地址开头 ws/wss url: 'ws://127.0.0.1:3000'}// 监控接入与否wx.onSocketOpen(()=>{// 接入成功wx.showToast({title: 'Socket接入成功',icon: 'success' })// 接入问候语})// 监控信息 - 待写// wx.onSocketMessage(()=>{ ... })// 监控关闭wx.onSocketClose(()=>{// 服务器Socket连接关闭wx.showToast({title: 'Socket已关闭'icon: 'error'})})// 监控错误wx.onSocketError((err)=>{console.log('接入异常',err);})})
3. 搭载Node服务器 - WebSocket服务器
搭载Node服务器,WebSocket服务是建立在HTTP上,所以需要先引用http模块创建HTTP服务器再创建WebSocket服务器,在这里使用第三方的websocket库。
# 初始化 —— 在创建好的目标目录下进行初始化
npm init -y
# 使用npm 安装 websocket 库
npm i websocket
# [可选] 使用 npm 安装 nodemon
npm i nodemon
* 不安装运行index.js文件使用每次修改后要执行 —— node index.js
* 安装运行index.js文件使用每次修改不需要手动执行
# 使用 npm 安装 moment.js —— 格式化时间
npm i moment -S
# 创建 index.js 文件进行编写代码
/* 服务器index.js文件 */ const http = require('http');
const WebSocketServer = require('websocket').server;
const moment = require('moment')// 创建HTTP服务器
const httpServer = http.createServer((req,res)=>{res.writeHead(404);res.end();
})// 创建websocket服务器
const wsServer = new WebSocketServer({httpServer,autoAcceptConnections : true // 自动接受连接
})// 监控接入
wsServer.on('connect',(connection)=>{// 监控数据信息messageconnection.on('message',(msg)=>{console.log('>> message' , msg);if( msg.type === 'utf8' ){var data = {content : '[自动回复] : 请等待用户接入回复...',date : moment(new Date()).format('YYYY-D-M , h:mm a')}connection.sendUTF( JSON.stringify(data) )}})// 监控关闭connection.on('close',()=>{console.log('Socket服务关闭');})})// 监听 3000 端口
httpServer.listen('3000',()=>{console.log('SERVER RUNNING ...')
})
4. 客户端发送数据信息
在input输入框中输入数据信息,通过在input框中绑定bindinput来监听获取输入框中的数据,将input框的value = {{ message }} ,便于做清空数据框操作。在点击"发送"按钮时将input中获取的信息通过sendSocketMessage 发送到服务器。
!--pages/wxchat/wxchat.js-->
Page({data:{head:{self:'https://profile.csdnimg.cn/8/D/A/2_weixin_52203618',server:'https://img2.baidu.com/it/u=4237756909,3713889849&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400'},},onLoad:function(){this.createSocketServer()},// 创建服务器连接createSocketServer(){...},message:'', // 待接收发送的消息// 获取Input框内的数据handleChange(opt){this.message = opt.detail.value}// 发送按钮触发事件handleSend(){// 发送的内容不为空if(this.message){// 发送wx.sendSocketMessage({data : this.message})// 收集我发出的信息 - 待写}else{wx.showToast({title: '请输入聊天内容',icon: 'none',duration: 1000})}}
})
5. 收集发送的数据信息
通过wx.sendSocketMessage()将内容发送出去,需要将内容进行一个收集,收集到一个数组当中去,然后在页面通过wx:for的渲染方式显示出来,下面是对发送的数据信息进行收集:
<!--pages/wxchat/wxchat.js-->
Page({data:{head:{ ... }, // 用户头像infoList:[] // 待收集数据信息},onLoad:function(){ ... },message: '', // 待发送的数据信息id: 0, // 数据信息标识// 发送按钮触发事件handleSend(){if(this.message){ wx.sendSocketMessage({ data: this.message })// 收集我发出的信息var list = this.data.infoListlist.push({id: this.id++ ,content: this.message ,role: 'self' // 标识 —— 客户端发出 })// 更新this.setData({infoList : listmessage : '' // 清空输入框})// 聊天视角 - 待写}else{wx.showToast({title: '请输入聊天内容',icon: 'none',duration: 1000})}}
})
6. 接入服务器问候语
接入服务器之后,客户端会自动发送 "你好" 的问候语;那么message是待发送的数据信息,监控客户端接入服务器的时候可以为message进行赋值,再调用handSend()方法发送,当然你可以调整一下handSend()方法,将其作为参数,这里就不做调整了,那么此时message绑定在input框的value值上,需要对message进行清空,否则当你再次点击发送的时候,没有输入任何数据也会发送 "你好" 的数据信息;
Page({data:{ ... },onLoad:function(){ this.createSocketServer() },// 创建服务器连接createSocketServer(){wx.connectSocket({ url: 'ws://127.0.0.1:3000/' })// 监控接入wx.onSocketOpen(() => {wx.showToast({title: 'Socket接入成功',icon: 'success'}) // 接入问候语this.message = '你好'this.handleSend()this.message = ''})...}
})
7. 服务器发送数据信息到客户端
客户端发送的数据信息在服务器中通过数据的监控可以知道,在前面已经编写了这段:
wsServer.on('connect',(connection)=>{// 监控是否有message信息connection.on('message',(msg)=>{console.log('>> message' , msg);if( msg.type === 'utf8'){ var data = {content: '[自动回复] : 请等待用户接入回复...',date: moment(new Date()).format('YYYY-D-M , h:mm a')}connection.sendUTF( JSON.stringify(data) )}})
})
接下来是在服务器进行发送数据到客户端,那么服务器可以通过在控制台的输入发送到客户端,监控控制台的输入进行获取通过sendUTF发送到客户端。
/* 服务器index.js文件 */ ...
wsServer.on('connect',(connection)=>{// 监控是否有message信息 connection.on('message',(msg)=>{ ... }) // 监控关闭connection.on('close',()=>{ ... })// 监控控制台输入process.stdin.on('data',(data)=>{var data = data.toString().trim() // 清除前后空格data = {content : data,date : moment(new Date()).format('YYYY-D-M , h:mm a')}connection.sendUTF(JSON.stringify(data)); // 发送})
})
...
服务器发送数据信息到客户端,那么需要在客户端进行监控数据信息
8. 客户端监控收集服务端发来的数据信息
服务器发来的数据信息,在小程序客户端上需要对其进行一个数据信息的监控,同时还需要将这些数据信息使用数组收集起来,在页面使用wx:for渲染交互信息。
<!--pages/wxchat/wxchat.js-->
Page({data:{ ... },onLoad:function(){ ... },message: '', // 待发送的数据信息id: 0, // 数据信息标识createSocketServer(){// 创建服务器连接 wx.connectSocket({url: 'ws://127.0.0.1:3000/'})// 监控接入wx.onSocketOpen(()=>{ ... })// 监控信息wx.onSocketMessage((msg)=>{var result = JSON.parse(msg.data);result.id = this.id++ result.role = 'Server' // 标识 —— 服务端发出 // 收集var list = this.data.infoListlist.push(result)// 更新this.setData({infoList : list})// 聊天视角 - 待写})}
})
9 .定位到消息处 —— 聊天视角
能够在页面上正常的渲染客户端和服务器所发出的数据信息,接下来需要处理聊天视角的问题,聊天界面的聊天数据随着发送的增多会是聊天界面出现滑动状态 scroll-view ,那么当超出的聊天数据信息就需要定位到最底部发出消息的视角,不需要手动的向下滑动到发送数据信息的位置。通过 scroll-view 的标签中的scroll-top属性,同时要对每一个发送数据信息的内容高度进行计算,然后进行定位,那么可以对渲染的每一个 content-li 的高度进行计算。
<!--pages/wxchat/wxchat.js-->
Page({data: { ...scrolltop: 0 // 默认滑动定位},onLoad:function(){ ... }message: '', // 待发送的数据信息id: 0, // 数据信息标识createSocketServer(){// 创建连接wx.connectSocket({ ... })// 监控接入wx.onSocketOpen(()=>{ ... })// 监控信息wx.onSocketMessage((msg)=>{...// 聊天视角this.rollingBottom()})},handleChange(opt){ ... },handleSend(){ if(this.message){...// 聊天视角this.rollingBottom()}else{...}},// 聊天视角调整方法rollingBottom(){wx.createSelectorQuery().selectAll('.content-li').boundingClientRect(results=>{ results.forEach(result=>{console.log(result)this.setData({scrollTop : result.bottom})})}).exec()}
})
10 .状态设置优化
当客户端点击发送数据信息,服务器都会返回这个自动回复的内容,显然不友好,需要什么效果呢,就是当服务器不在线状态时,客户端发送什么,服务器仅回复【自动回复】来提示客户端当前不在线,如果服务器在控制台输出作出答复返回给客户端,此时服务器就是在线状态,那么当客户端再次发来的时候,服务器就不再继续返回【自动回复】
/* 服务器index.js文件 - 完整 */ const http = require('http');
const WebSocketServer = require('websocket').server;
const moment = require('moment')// 在线状态
let onLineStatus = false // 服务器默认不在线// 创建Http服务器
const httpServer = http.createServer((req,res)=>{res.writeHead(404);res.end();
})// 创建ws服务器
const wsSercer = new WebSocketServer({httpServer,autoAcceptConnections : true
})
wsSercer.on('connect',(connection)=>{// 监控是否有message信息connection.on('message',(msg)=>{console.log('>> message',msg)// if( msg.type === 'utf8'){if( msg.type === 'utf8' && !onLineStatus){var data = {content: '[自动回复] : 请等待用户接入回复...',date: moment(new Date()).format('YYYY-D-M , h:mm a')}connection.sendUTF( JSON.stringify(data) )}})// 监控关闭connection.on('close',()=>{console.log('close')})// 接收控制台输入process.stdin.on('data',(data)=>{var data = data.toString().trim() // 清除前后空格data = {content : data,date : moment(new Date()).format('YYYY-D-M , h:mm a')}connection.sendUTF( JSON.stringify(data) )// 在线状态onLineStatus = true})
})httpServer.listen('3000',()=>{console.log('SERVER RUNNING ...')
})
以上就完成了整个使用WebSocket实现通信的代码,下面来进行测试一下吧效果吧!
测试效果
1. 启动服务器 node index.js / nodemon index.js
2. 刷新一下微信小程序
3. 在控制台上输入发送数据信息
4. 客户端接收并回复
以上就是这篇文章的全部内容了,这里可以借用局域网的url地址更换,可以实现一个简单的聊天室功能,那么本篇就到这,感谢大家的支持!!!
相关文章:

微信小程序 WebSocket 通信 —— 在线聊天
在Node栏目就讲到了Socket通信的内容,使用Node实现Socke通信,还使用两个流行的WebSocket 库,ws 和 socket.io,在小程序中的WebSocket接口和HTML5的WebSocket基本相同,可以实现浏览器与服务器之间的全双工通信。那么本篇…...

VMware快照:简化虚拟化环境管理与数据保护
引言: 在虚拟化环境中,数据保护和灵活性是至关重要的。VMware快照作为一项强大的功能,为虚拟机管理者提供了便利和安全性。本文将介绍VMware快照的使用,以及它为用户带来的几个关键优势。 VMware快照是一项重要的功能,…...

图的最短路径
最短路径算法对图结构没有特殊要求,不要求连通图,且有向图无向图均可。 最短路径算法目标是求得从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径。解决最短路的问题有以下算法:Dijkst…...
RHCE----Shell变量和引用
1.变量的类型及含义 变量类型: 1、自定义变量: 在当前的shell命令行界面设置的变量是局部变量 例子: num1 namezhangsan 2、环境变量全局变量,通过export 导出后的局部变量是全局变量 、bash的初始化文件:/etc/profile:存放一些全局变量…...

【Redis】聊一下缓存雪崩、击穿、穿透、预热
缓存的引入带来了数据读取性能的提升,但是因此也引入新的问题,一个是数据双写一致性,另一个就是雪崩、击穿、穿透,那么如何解决这些问题,我们来说下对应的问题和解决方案 雪崩 缓存雪崩:同一时间内大量请…...

全景描绘云原生技术图谱,首个《云原生应用引擎技术发展白皮书》发布
5月12日,由神州数码主办、北京经开区国家信创园、中关村云计算产业联盟协办的2023通明湖论坛-云原生分论坛在京召开。论坛期间,神州数码联合北京通明湖信息技术应用创新中心、中国信通院和通明智云正式发布了《云原生应用引擎技术发展白皮书》࿰…...

【Python共享文件】——Python快速搭建HTTP web服务实现文件共享并公网远程访问
文章目录 1. 前言2. 视频教程3. 本地文件服务器搭建3.1 python的安装和设置3.2 cpolar的安装和注册 4. 本地文件服务器的发布4.1 Cpolar云端设置4.2 Cpolar本地设置 5. 公网访问测试6. 结语 1. 前言 数据共享作为和连接作为互联网的基础应用,不仅在商业和办公场景有…...
Mysql数据库分库分表
为什么使用分库分表? 传统的将数据集中存储至单一数据节点的解决方案,在性能、可用性和运维成本这三方面已经难于满足互联网的海量数据场景。 1)性能 从性能方面来说,由于关系型数据库大多采用 B 树类型的索引,在数…...

SpringBoot热部署插件原理分析及实战演练
目录 1、关于热部署(Hot Deploy)产生的背景 1)热部署出现前 2)热部署出现后 2、spring-boot-devtools插件原理 1)解决变更文件自动加载到JVM中 2)spring-boot-devtools重启速度比手动重启快 3、关于…...

【C++ 入坑指南】(10)函数
文章目录 简介定义实例函数的分文件编写 简介 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定…...

P2233 [HNOI2002]公交车路线
题目描述 在长沙城新建的环城公路上一共有 8 个公交站,分别为 A、B、C、D、E、F、G、H。公共汽车只能够在相邻的两个公交站之间运行,因此你从某一个公交站到另外一个公交站往往要换几次车,例如从公交站 A 到公交站 D,你就至少需要…...
java入门-W11(K168-K182)网络编程
1. 网络编程入门 1.1 网络编程概述 计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统…...

距离6月18日DAMA-CDGA/CDGP认证考试还有31天,报名从速
6月18日DAMA-CDGA/CDGP数据治理认证考试开放报名中! 考试开放地区:北京、上海、广州、深圳、长沙、呼和浩特、杭州、南京、济南、成都、西安。其他地区凑人数中… DAMA-CDGA/CDGP数据治理认证班进行中,报名从速! DAMA认证为数据管…...
PO、VO、DAO、BO、DTO、POJO区分
一 分层领域模型规约: DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。BO(Business Object):业务对象,由 Service 层输出的封装…...

MobPush Flutter平台插件
集成准备 注册账号 使用PushSDK之前,需要先在MobTech官网注册开发者账号,并获取MobTech提供的AppKey和AppSecret,详情可以点击查看注册流程 MobPush后台配置 注册MobTech账号后,需要在MobTech后台进行相关信息的配置ÿ…...
机器学习面试题库:K-means
一、简述K-means算法的原理及工作流程? 原理: K-means是一个无监督的聚类算法。它的主要目的是对同一组数据对象进行分类。其原理是基于样本间的相似性来聚类分析的,即将所有样本分为K个簇,使得同一个簇间中样本相似性最高&#…...

Linux:文本三剑客之awk
Linux:文本三剑客之awk 一、awk编辑器1.1 awk概述1.2 awk工作原理1.3 awk与sed的区别 二、awk的应用2.1 命令格式2.2 awk常见的内建变量(可直接用) 三、awk使用3.1 按行输出文本3.2 按字段输出文本3.3 通过管道、双引号调用 Shell 命令 一、a…...

如何借助Kafka持久化存储K8S事件数据?
大家应该对 Kubernetes Events 并不陌生,特别是当你使用 kubectl describe 命令或 Event API 资源来了解集群中的故障时。 $ kubectl get events15m Warning FailedCreate …...

一种基于非均匀分簇和建立簇间路由的算法的无线传感器网络路由协议(Matlab代码实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨💻4 Matlab代码 💥1 概述 本文准备了一种路由方法,该方法使传感器通过有效地使用能量将数据从发送方加载到接收器,因为它在 LEAC…...

usb摄像头驱动打印信息
usb摄像头驱动打印信息 文章目录 usb摄像头驱动打印信息 在ubuntu中接入罗技c920摄像头打印的信息如下: [ 100.873222] usb 3-2: new high-speed USB device number 5 using xhci_hcd [ 101.230728] usb 3-2: New USB device found, idVendor046d, idProduct08e5 …...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...