最近在用Vue和uniapp写websocket 中间遇到了好多问题
先说说在h5中发送socket
1.Vue写的h5页面中使用websocket
1.1封装命名为cysocket.js
的文件 内容如下:
export function CySocket(config) {
this.server = config ? config.server || '' : ''
this.autoReconnect = config ? config.autoReconnect || '' : true
var socket
var times = 0// 尝试连接三次
var interval
var cySocket = this
this.init = function(callback) {
if (this.server === '') {
console.error('服务器地址不能为空:)')
return
}
if (interval) {
clearInterval(interval)
interval = null
times = 0
}
try {
if (socket && socket.readyState === 1) return
socket = new WebSocket(this.server)
socket.onopen = function(data) {
if (config && config.openSocket) config.openSocket.call(cySocket, data)
}
socket.binaryType = 'arraybuffer'
socket.onmessage = function(msg) {
if (config && config.receiveMsg) config.receiveMsg.call(cySocket, msg.data)
}
interval = setInterval(function() {
if (socket.readyState !== 1) {
if (times > 2) {
clearInterval(interval)
interval = null
times = 0
console.log('无法连接到服务器...:)')
return
} else {
console.log('通讯异常,正在尝试重连...:)')
if (socket) {
socket.close()
}
socket = new WebSocket(config.server)
}
times++
} else {
clearInterval(interval)
interval = null
times = 0
console.log('已经连接到服务器')
}
}, 5000)
if (typeof (callback) === 'function') callback.call(cySocket)
} catch (e) {
console.log(e.message)
if (this.autoReconnect) {
this.init(callback)
}
}
}
this.close = function() {
if (interval) {
clearInterval(interval)
interval = null
times = 0
}
if (socket && socket.readyState === 1) {
socket.close()
}
}
// 字符串转byte(防止中问传过去出现乱码的情况)
function strToByte(str) {
var bytes = new Array();
var len, c;
len = str.length;
for(var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if(c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
return bytes;
}
// 这里是对我们发送过去的代码进行处理
this.sendMsg = function(data) {
if (!socket || socket.readyState !== 1) {
this.init(config)
}
var byte=[]
byte = strToByte(data)
var arrayBuffer = new ArrayBuffer(byte.length + 4)
var dataView = new DataView(arrayBuffer)
dataView.setInt32(0, byte.length, true) // 指定小端,主机字节序设置
for (var i = 0; i < byte.length; i++) {
dataView.setUint8(i + 4, byte[i])
}
if (socket && socket.readyState === 1) { socket.send(dataView.buffer) }
}
this.checkSocketState = function() {
return socket.readyState
}
}
1.2 在文件中引用
// 引入1.1中封装的文件
import { CySocket } from '../../../static/CySocket'
method中的方法如下
// 1.实例化socket 发送登陆心跳
creatsocket() {
const that = this
Vue.prototype.cySocket = new CySocket({
server: `ws://59.1.1.19:9128`, // 此为测试地址 用的时候需要写成后台提供的地址
autoReconnect: false,
openSocket: function() {
// 2.obj为需要给后台传的参数
const obj = {
cmdCode: '0201',
oppositeId: that.openid,
timeToken: new Date().getTime()
}
const str = JSON.stringify(obj)
// 3.根据要求通过this.sendMsg() 把需要上传的参数传递过去
this.sendMsg(str)
},
// 监听socket的返回
receiveMsg: function(data) {
// data返回的数据类型看后台是如何返回的
// 将收到的数据转换为字符串,这里是arraybuffer数据的转换
that.ab2str(data, function(str) {
var obj = JSON.parse(str)
// 将收到的 数据保存在vuex中
that.$store.commit('SET_RESULT_CODE', obj)
Vue.prototype.code = that.$store.state.socket.resultCode
})
}
})
Vue.prototype.cySocket.init()
},
// arraybuffer转字符串
ab2str(u, f) {
var b = new Blob([u])
var r = new FileReader()
r.readAsText(b, 'utf-8')
r.onload = function() {
if (f)f.call(null, r.result)
}
},
formatdate() {
window.Date.prototype.format = window.Date.prototype.format || function(fmt) {
var o = {
'M+': this.getMonth() + 1, // 月份
'd+': this.getDate(), // 日
'h+': this.getHours(), // 小时
'm+': this.getMinutes(), // 分
's+': this.getSeconds(), // 秒
'q+': Math.floor((this.getMonth() + 3) / 3), // 季度
'f': this.getMilliseconds() // 毫秒
}
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length))
for (var k in o) {
if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
}
return fmt
}
},
1.3在其他页面中使用socket
注意: 1.2中的代码是在准备给socket发送登陆心跳的时候写的 写一次之后以后的界面就可以直接发送不需要在建立连接了
// 有车牌出场
// 出场抬杆
async raiseUp() {
const raise = {
cmdCode: '0206',
gate_id: this.gate_id,
oppositeId: this.openid,
identity: this.parkNo,
timeToken: new Date().getTime()
}
const raiseStr = JSON.stringify(raise)
// 发送数据
this.cySocket.sendMsg(raiseStr)
// 对接收到的数据进行处理
await this.status()
},
status() {
// 根据自己业务需求写
setTimeout(() => {
const code = this.$store.state.socket.resultCode
// console.log(code, 'rasiUp')
if (code.resultCode === 1 && code.cmdCode === '0106') {
// 需要把收到的参数传给支付界面渲染
console.log(code, 'rasiUp')
if (!this.ypcout) {
this.ypcout = code
this.carOutMsg = {}
console.log(this.ypcout)
} else {
code.totalFee ? this.carOutMsg.TotalFee = code.totalFee : ''
}
this.pay = true
this.success = false
// code.totalFee ? this.carOutMsg['TotalFee'] = code.totalFee : ''
} else if (code.cmdCode === '0106' && code.resultCode === 0) {
this.trouble = this.fail = true
return
} else if (code.cmdCode === '0106') {
this.trouble = this.fail = true
return
} else {
this.fail = true
this.answere = true
return
}
}, 1000)
}
1.4 项目中遇到的问题
遇到了两个问题:
- 发送时候中文乱码问题
问题描述
:发送socket的需求是 转换为Unicode编码
转换的过程中遇到了中文 导致后台接收的时候中文乱码
下图为协议文档说明:
image.png
解决方法
: 见下方1.4.1
- 发送时候中文乱码问题
- 2.接收时候ArrayBuffer 包含中文 转换的问题
1.4.1 发送时候中文乱码问题
在发送过去的代码中使用这个方法
// 字符串转byte(防止中问传过去出现乱码的情况)
function strToByte(str) {
var bytes = new Array();
var len, c;
len = str.length;
for(var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if(c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
return bytes;
}
使用
在上边定义的cysocket.js
文件中的sendMsg函数中进行操作
this.sendMsg = function(data) {
if (!socket || socket.readyState !== 1) {
this.init(config)
}
var byte=[]
byte = strToByte(data)
var arrayBuffer = new ArrayBuffer(byte.length + 4)
var dataView = new DataView(arrayBuffer)
dataView.setInt32(0, byte.length, true) // 指定小端,主机字节序设置
for (var i = 0; i < byte.length; i++) {
dataView.setUint8(i + 4, byte[i])
}
if (socket && socket.readyState === 1) { socket.send(dataView.buffer) }
}
1.4.2 接收到ArrayBuffer转换的问题
先说说ArrayBuffer是什么?
ArrayBuffer
对象用来表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer
不能直接操作,而是要通过类型数组对象或 DataView
对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
我是如何操作的?
- 具体用法可以参考1.2总的
receiveMsg
调用下方这个ab2str
// arraybuffer转字符串
ab2str(u, f) {
var b = new Blob([u])
var r = new FileReader()
r.readAsText(b, 'utf-8')
r.onload = function() {
if (f)f.call(null, r.result)
}
},
用法
// 监听socket的返回
receiveMsg: function(data) {
// data返回的数据类型看后台是如何返回的
// 将收到的数据转换为字符串,这里是arraybuffer数据的转换
that.ab2str(data, function(str) {
var obj = JSON.parse(str)
// 将收到的 数据保存在vuex中
that.$store.commit('SET_RESULT_CODE', obj)
Vue.prototype.code = that.$store.state.socket.resultCode
})
}
网友评论