出于信息安全的原因,以往浏览器是无法直接控制电脑上的设备的,所以,很多黑客为了控制其他电脑,一般就是做一段木马程序,通过各种办法引诱上网小白们通过浏览器下载安装一堆堆的垃圾软件,其中很有可能就有一段木马病毒通过这种方式植入到了电脑。中了木马病毒的设备就象一只待宰的羔羊,黑客们可以通过木马病毒控制电脑,偷取电脑内的数据、破坏电脑系统。在这种情况下,浏览器做为上网的窗口,通过它来展现远端未知来历的网页,如果它可以直接控制电脑上的设备是一件很有安全隐患的事情。
然而,随着互联网的发展,以B/S结构开发程序越来越占主流,B/S结构的软件都通过浏览器与操作人员交互数据,这又使得通过浏览器来控制本电脑的设备变得越来越有需求。
你看,这是不是非常矛盾的一种状态?
但是,需求一定是占主导地位的,需求推动整个世界的前进,所以,通过许多大神的辛勤、努力,开发了一些由浏览器控制电脑设备的工具,Web Serial Api就是其中的一个,它可以通过浏览器直接向电脑上的串口设备发送指令,从而控制通过串口连接的电脑外设。
出于安全考虑,浏览器连接串口前,都会跳出提示窗口让使用者确认授权连接指定的串口。
以下Javascript代码展示了通过 Web Serial Api ,浏览器打开指定串口,向串口发送读、写M1卡的指令完成读写M1卡操作。
本示例使用的设备:RS232串口RFID NFC IC卡读写器可二次开发编程发卡器USB转COM-淘宝网 (taobao.com)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Serial串口读写器示例 </title>
<script>
window.onload = function() {
document.getElementById('butt_openserial').hidden=true;
document.getElementById('butt_closeserial').hidden=true;
}
if ('serial' in navigator){
}else{
alert('您的浏览器不支持 Web Serial API,暂无法使用以下功能!');
}
navigator.serial.onconnect =function event(){
console.log("Serial port connected: ", event.target);
}
navigator.serial.ondisconnect =function event(){
console.log("Serial port disconnected: ", event.target);
}
var BLOCK0_EN = 0x01;//读第一块的(16个字节)
var BLOCK1_EN = 0x02;//读第二块的(16个字节)
var BLOCK2_EN = 0x04;//读第三块的(16个字节)
var NEEDSERIAL = 0x08;//仅读指定序列号的卡
var EXTERNKEY = 0x10;//用明码认证密码,产品开发完成后,建议把密码放到设备的只写区,然后用该区的密码后台认证,这样谁都不知道密码是多少,需要这方面支持请联系
var NEEDHALT = 0x20; //读/写完卡后立即休眠该卡,相当于这张卡不在感应区。要相重新操作该卡必要拿开卡再放上去
var port = null;
var reader = null;
var reading = false;
const getdata=new Uint8Array(1000); //接收串口返回的数据
var DataPoint=0; //接收数据指针
var SendCode=0; //已发送的指令代码
function isUIntNum(val) {
var testval = /^\d+$/; // 非负整数
return (testval.test(val));
}
function isHex(val) {
var testval = /^(\d|[A-F]|[a-f])+$/; // 十六进制数判断
return (testval.test(val));
}
async function SelectSerial(){
try{
port =await navigator.serial.requestPort(); // 弹出系统串口列表对话框,选择一个串口进行连接
ports =await navigator.serial.getPorts(); // 获取已连接的授权过的设备列表
document.getElementById('butt_openserial').hidden=false;
}
catch (e)
{
console.log(e);
}
}
function updateInputData(data) {
let array = new Uint8Array(data); // event.data.buffer就是接收到的inputreport包数据了
//let hexstr = "";
for (const data of array) {
//hexstr += (Array(2).join(0) + data.toString(16).toUpperCase()).slice(-2) + " "; // 将字节数据转换成(XX )形式字符串
getdata[DataPoint]=data;
DataPoint=DataPoint+1;
}
var crc=0;
for(i=1;i<DataPoint;i++){ //校验接收数据,同时也解决数据分包上传的问题
crc=crc^getdata[i];
}
if (crc==0 && DataPoint>1){
let hexstr = "";
for (i=0;i<DataPoint;i++){
hexstr=hexstr+getdata[i].toString(16).padStart(2, '0').toUpperCase()+" ";
}
ReceiveData.value += hexstr;
var dispstr="";
var cardnohex="";
var datahex="";
switch (SendCode) {
case 1:
break;
case 2: //读取M1卡序列号的回应
case 3: //读取M1卡扇区数据的回应
case 4: //写M1卡扇区数据的回应
case 5: //修改M1卡扇区数密钥的回应
switch (getdata[0]){
case 1: //返回有效数据长度为1
switch (getdata[1]){
case 8:
dispstr = "未寻到卡!" ;
break;
case 9:
dispstr = "两张以上卡片同时在感应区,发生冲突!" ;
break;
case 10:
dispstr = "无法选择激活卡片!" ;
break;
case 11:
dispstr = "密码装载失败,卡片序列号已知!" ;
break;
default:
dispstr = "操作卡失败,返回代码:" + getdata[1].ToString() ;
break;
}
var label_disp = document.getElementById('label_disp');
label_disp.innerText = dispstr;
break;
case 5: //返回有效数据长度为5
for(i=2;i<6;i++){
cardnohex=cardnohex+getdata[i].toString(16).padStart(2, '0').toUpperCase();
}
if(SendCode==2){
dispstr =