概要
高拍仪硬件通信原理,WebSocket源码解析(JavaScript)
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
ps:本来想写sm4加密和解密算法的,但是sm3一个加密都看的我头昏昏的,就先不为难自己了,说说WebSocket的源码解析吧
源自: HTML5 WebSocket | 菜鸟教程 (runoob.com)
整体架构流程
拟人化展示从HTTP协议升级到WebSocket协议的过程:
1、发送一个GET请求
关键: Upgrade: websocket; Connection: Upgrade;
这两个就告诉服务器,我要发起websocket协议,我不是HTTP。
2、服务器收到了协议,返回一个 Switching Protocol, 这样就连接成功了。
3、接下来的通信都是websocket, 这样就很好的连接了。
源自:WebSocket建立连接的过程_websocket如何建立连接-CSDN博客
技术名词解释
连接请求:Connection: Upgrade;
通信消息数据:(二进制消息)
技术细节
一、建立连接:
在高拍仪初始化时建立连接
初始化,设置显示相机屏幕大小,设置自动裁剪(初始化后才能设置)
function LoadCameraDocument() {
if (!window.WebSocket) {
alert("浏览器不支持HTML5,请更新浏览器或者使用其它浏览器");
}
//console.log("LoadCameraDocument");
var obj = document.getElementById("CameraCtl");
Cam_ControlInit(obj, 0, 0, 600, 400);
// 模拟异步硬件初始化
setTimeout(function () {
// 设置自动裁剪
SetCameraCutMode();
}, 2500); // 假设2.5秒后硬件初始化完成
// 模拟异步硬件初始化
setTimeout(function () {
// 设置自动裁剪
SetCameraCutMode();
}, 5000); // 完不成再来一下
}
连接WebSocket,初始化相机
//*************摄像头操作初始化***************
function Cam_ControlInit(documentObj, mX, mY, mwidth, mheight) {
WebSocketConnect();
InitCanvas(documentObj, mX, mY, mwidth, mheight);
//console.log("Cam_ControlInit");
}
设置连接地址和开启时函数,
设置心跳检测、断线重连、获取设备数目
完成后输出连接成功 "socket.onopen"
socket = new WebSocket("ws://127.0.0.1:22225");
socket.binaryType = "arraybuffer";
socket.onopen = function (event) {
//heartCheck.reset().start();
heartCheck();
isSocketConnect = true;
clearInterval(intervalId);
//if (isOpenMainCamera == false)
Cam_GetDevCount();
console.log("socket.onopen");
};
二、发送数据:
首先要有心跳,if (isSocketConnect)
其次,创建二进制数组 var aDataArray = new Uint8Array(totalLen)
然后,消息分装
最后,发送二进制消息 socket.send(aDataArray.buffer);
示例“处理拍照逻辑发送消息逻辑”代码:
function CaptureImage(fileAddr) {
if (isSocketConnect) {
// var pathArray = stringToUint8Array(fileAddr);
if (fileAddr == "") {
var packageCount = 1;
var len = 0;
var pindex = 0;
var totalLen = 12;
var aDataArray = new Uint8Array(totalLen);
aDataArray[0] = 0x77;
aDataArray[1] = 0x88;
aDataArray[2] = 0x10;
aDataArray[3] = 0x00;
aDataArray[4] = len >> 16 & 0xff;
aDataArray[5] = len >> 8 & 0xff;
aDataArray[6] = len & 0xff;
aDataArray[7] = packageCount >> 8 & 0xff; //包总数
aDataArray[8] = packageCount & 0xff; //包总数
aDataArray[9] = 0; //分包长度
aDataArray[10] = pindex >> 8 & 0xff; //包序号
aDataArray[11] = pindex & 0xff; //包序号
console.log("pindex:" + pindex);
socket.send(aDataArray.buffer);
} else {
var path = encodeURI(fileAddr);
//console.log(path);
var pathArray = stringToByte(path);
var len = pathArray.length;
var packageCount = 0;
var tmpLen = len;
while (tmpLen > 0) {
tmpLen = tmpLen - 90;
packageCount++;
}
console.log("packageCount:" + packageCount);
var pindex = 0;
tmpLen = len;
while (tmpLen > 0) {
tmpLen = tmpLen - 90;
if (tmpLen > 0) {
var totalLen = 90 + 12;
var aDataArray = new Uint8Array(totalLen);
aDataArray[0] = 0x77;
aDataArray[1] = 0x88;
aDataArray[2] = 0x10;
aDataArray[3] = 0x00;
aDataArray[4] = len >> 16 & 0xff;
aDataArray[5] = len >> 8 & 0xff;
aDataArray[6] = len & 0xff;
aDataArray[7] = packageCount >> 8 & 0xff; //包总数
aDataArray[8] = packageCount & 0xff; //包总数
aDataArray[9] = 90; //分包长度
aDataArray[10] = pindex >> 8 & 0xff; //包序号
aDataArray[11] = pindex & 0xff; //包序号
console.log("pindex:" + pindex);
for (var i = 0; i < 90; i++) {
aDataArray[12 + i] = pathArray[i + pindex * 90];
}
socket.send(aDataArray.buffer);
} else {
var totalLen = 90 + tmpLen + 12; // 此时tmpLen为负数,做加法运算
var aDataArray = new Uint8Array(totalLen);
aDataArray[0] = 0x77;
aDataArray[1] = 0x88;
aDataArray[2] = 0x10;
aDataArray[3] = 0x00;
aDataArray[4] = len >> 16 & 0xff;
aDataArray[5] = len >> 8 & 0xff;
aDataArray[6] = len & 0xff;
aDataArray[7] = packageCount >> 8 & 0xff; //包总数
aDataArray[8] = packageCount & 0xff; //包总数
aDataArray[9] = 90 + tmpLen; //分包长度
aDataArray[10] = pindex >> 8 & 0xff; //包序号
aDataArray[11] = pindex & 0xff; //包序号
console.log("pindex:" + pindex);
for (var i = 0; i < (90 + tmpLen); i++) {
aDataArray[12 + i] = pathArray[i + pindex * 90];
}
socket.send(aDataArray.buffer);
}
pindex++;
toSleep(80);
}
}
}
}
三、响应数据:
例如:拍照时数据处理
创建onmessage方法,心跳检测必写
socket.onmessage = function (event) {
// heartCheck.reset().start();
var rDataArr = new Uint8Array(event.data);
if (rDataArr.length > 0) {
// WebSocket心跳检测
if (rDataArr[0] == 0x11 && rDataArr[1] == 0x11 && rDataArr[2] == 0x11) {
console.log("socket心跳 ❤");
}
// 处理rDataArr数据
// 省略...示例代码
}
}
响应数据方法:socket.onmessage = function (event) {}
示例"处理拍照结果返回响应逻辑"代码:
//拍照结果返回
if (rDataArr[2] == 0x10) {
var flag;
if (rDataArr[3] == 0x01) {
flag = 0;
var imgpathLen = rDataArr[4] * 256 + rDataArr[5];
if (imgpathLen == 0) {
var base64Len = rDataArr[6] * 65536 + rDataArr[7] * 256 + rDataArr[8];
var imgPathStr = "";
var base64Data = new Uint8Array(base64Len);
for (var i = 0; i < base64Len; i++) {
base64Data[i] = rDataArr[9 + imgpathLen + i];
}
var base64Str = Uint8ArrayToString(base64Data);
GetCaptrueImgResultCB(flag, imgPathStr, base64Str);
} else {
var base64Len = rDataArr[6] * 65536 + rDataArr[7] * 256 + rDataArr[8];
var pData = new Uint8Array(imgpathLen);
for (var i = 0; i < imgpathLen; i++) {
pData[i] = rDataArr[9 + i];
}
var str = byteToString(pData);
var imgPathStr = decodeURIComponent(str);
var base64Data = new Uint8Array(base64Len);
for (var i = 0; i < base64Len; i++) {
base64Data[i] = rDataArr[9 + imgpathLen + i];
}
var base64Str = Uint8ArrayToString(base64Data);
GetCaptrueImgResultCB(flag, imgPathStr, base64Str);
}
}
if (rDataArr[3] == 0x02) {
flag = 2;
GetCaptrueImgResultCB(flag, "", "");
}
}
小结
WebSocket是一种在Web浏览器和服务器之间进行全双工通信的协议,通过它可以实现实时的数据传输。建立WebSocket连接的过程包括发送一个GET请求并指定协议升级,服务器返回一个Switching Protocol响应,连接成功后即可进行WebSocket通信。在实际应用中,可以通过WebSocket发送和接收二进制消息来实现各种功能,例如拍照、传输文件等。需要注意的是,为了保持连接的稳定性,通常会实现心跳检测和断线重连功能。
-
深入理解WebSocket协议:通过实际编码,我更深入地理解了WebSocket协议的工作原理和建立连接的过程。
-
网络通信能力提升:通过处理WebSocket通信的逻辑,我提升了自己的网络通信能力,包括发送和接收数据的处理能力。
-
异步编程理解:WebSocket通信通常是异步的,我巩固了如何处理异步通信,例如通过回调函数处理接收到的消息。
-
实践经验:通过实际编写WebSocket通信相关的代码,我积累了宝贵的实践经验,可以帮助我更好地理解和应用相关知识。
-
问题解决能力:在编写过程中可能遇到了各种问题,通过解决这些问题,我提升了自己的问题解决能力和调试技巧。