在物联网应用开发中,实时数据监控一直是提升用户体验的关键。传统HTTP轮询方式虽然实现简单,但存在明显的性能瓶颈——高延迟、高流量消耗和服务器压力大。本文将带你从零实现基于WebSocket的温湿度实时监控系统,彻底告别低效的轮询模式。
WebSocket是一种全双工通信协议,建立连接后客户端和服务端可以随时主动推送消息。相比之下,HTTP轮询需要客户端不断发起请求"询问"服务端是否有新数据。
性能对比表格:
| 特性 | WebSocket | HTTP轮询 |
|---|---|---|
| 连接方式 | 持久化单连接 | 频繁建立/断开连接 |
| 数据传输方向 | 双向实时通信 | 单向请求-响应 |
| 延迟 | 毫秒级 | 取决于轮询间隔 |
| 流量消耗 | 仅传输有效数据 | 每次请求都含HTTP头信息 |
| 服务器压力 | 连接数稳定 | 高频连接建立/销毁 |
WebSocket特别适合以下场景:
提示:OneNet平台同时支持HTTP API和MQTT协议,但微信小程序环境对MQTT支持有限,WebSocket成为最佳选择。
在开始编码前,需要完成OneNet平台的以下配置:
javascript复制// OneNet WebSocket连接基本信息
const deviceConfig = {
productId: 'your_product_id',
deviceName: 'your_device_name',
apiKey: 'your_api_key',
dataPoints: ['temperature', 'humidity'] // 根据实际数据流名称修改
}
OneNet WebSocket连接采用Token鉴权,需要按照平台规则生成连接参数:
python复制# Python示例:生成OneNet WebSocket连接Token
import time
import hashlib
import hmac
def generate_token(product_id, device_name, api_key):
version = '2020-05-29'
res = f'products/{product_id}/devices/{device_name}'
et = str(int(time.time()) + 3600) # 1小时后过期
method = 'sha1'
key = api_key.encode('utf-8')
to_sign = f'{et}\n{method}\n{res}\n{version}'.encode('utf-8')
sign = hmac.new(key, to_sign, hashlib.sha1).hexdigest()
return f'version={version}&res={res}&et={et}&method={method}&sign={sign}'
微信小程序提供了完整的WebSocket API,以下是建立连接的核心代码:
javascript复制// pages/index/index.js
Page({
data: {
temperature: 0,
humidity: 0,
socketStatus: 'disconnected'
},
connectWebSocket() {
const that = this
const token = generateToken(deviceConfig) // 使用前面提到的鉴权方法
this.socketTask = wx.connectSocket({
url: `wss://mqtts.heclouds.com:8084?${token}`,
success(res) {
console.log('WebSocket连接建立成功')
that.setData({ socketStatus: 'connecting' })
},
fail(err) {
console.error('连接失败:', err)
}
})
// 监听连接打开事件
this.socketTask.onOpen(() => {
that.setData({ socketStatus: 'connected' })
console.log('WebSocket连接已打开')
})
// 监听消息接收
this.socketTask.onMessage((res) => {
const data = JSON.parse(res.data)
if(data.type === 'data') {
that.processSensorData(data.payload)
}
})
},
processSensorData(payload) {
// 根据OneNet数据格式解析温湿度值
this.setData({
temperature: payload.temperature,
humidity: payload.humidity
})
}
})
网络不稳定是移动端常见问题,完善的断线重连策略至关重要:
javascript复制// 在Page对象中添加
reconnectTimer: null,
maxReconnectAttempts: 5,
reconnectAttempts: 0,
onSocketClose() {
const that = this
if(this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectTimer = setTimeout(() => {
console.log(`尝试重连,第${this.reconnectAttempts+1}次`)
this.connectWebSocket()
this.reconnectAttempts++
}, 3000) // 3秒后重连
} else {
wx.showToast({
title: '连接断开,请检查网络',
icon: 'none'
})
}
},
// 在connectWebSocket方法中添加关闭监听
this.socketTask.onClose(() => {
that.setData({ socketStatus: 'disconnected' })
that.onSocketClose()
})
保持连接活跃,防止被运营商或防火墙断开:
javascript复制// 在connectWebSocket方法中添加
this.heartbeatInterval = setInterval(() => {
if(this.socketTask && this.data.socketStatus === 'connected') {
this.socketTask.send({
data: JSON.stringify({ type: 'ping' }),
success() {
console.log('心跳发送成功')
}
})
}
}, 30000) // 每30秒发送一次心跳
// 记得在onUnload中清除
onUnload() {
if(this.heartbeatInterval) clearInterval(this.heartbeatInterval)
if(this.socketTask) this.socketTask.close()
}
当监控点增多时,可以考虑数据优化方案:
javascript复制// 示例:简单差值传输实现
let lastValues = {}
function processData(payload) {
const changedData = {}
for(const key in payload) {
if(lastValues[key] !== payload[key]) {
changedData[key] = payload[key]
lastValues[key] = payload[key]
}
}
return Object.keys(changedData).length ? changedData : null
}
确保UI流畅更新:
javascript复制// 优化后的setData调用
let renderTimer = null
function updateUI(data) {
if(renderTimer) clearTimeout(renderTimer)
renderTimer = setTimeout(() => {
this.setData(data)
renderTimer = null
}, 100) // 每100ms最多更新一次
}
javascript复制// 示例:数据校验函数
function validateData(data) {
if(!data || typeof data !== 'object') return false
if(data.temperature < -50 || data.temperature > 100) return false
if(data.humidity < 0 || data.humidity > 100) return false
return true
}
javascript复制// 简单的监控统计
const stats = {
totalReceived: 0,
lastError: null,
startTime: Date.now()
}
function updateStats(type, payload) {
if(type === 'data') {
stats.totalReceived++
} else if(type === 'error') {
stats.lastError = {
time: Date.now(),
message: payload.message
}
}
}
在实际项目中,WebSocket实现最容易被忽视的是异常处理。我曾遇到过一个案例:用户反馈数据偶尔会停止更新,排查后发现是心跳包发送失败后没有正确处理重连逻辑。后来我们增加了心跳超时检测和多重重试机制,稳定性得到了显著提升。