图形的力量,websocket探索其与语音

websocket探索其与话音、图片的能力

2015/12/26 · JavaScript
· 3 评论 ·
websocket

原文出处:
AlloyTeam   

说到websocket想比我们不会陌生,假若陌生的话也没提到,一句话概括

“WebSocket protocol
是HTML5一种新的商谈。它已毕了浏览器与服务器全双工通讯”

WebSocket相相比较传统那个服务器推技术俨然好了太多,大家得以挥手向comet和长轮询那个技术说拜拜啦,庆幸我们生存在所有HTML5的时代~

那篇小说大家将分三有些探索websocket

首先是websocket的普遍使用,其次是一心自己打造服务器端websocket,最终是重中之重介绍利用websocket制作的五个demo,传输图片和在线语音聊天室,let’s
go

一、websocket常见用法

那里介绍三种自己认为大规模的websocket已毕……(专注:本文建立在node上下文环境

1、socket.io

先给demo

JavaScript

var http = require(‘http’); var io = require(‘socket.io’); var server =
http.createServer(function(req, res) { res.writeHeader(200,
{‘content-type’: ‘text/html;charset=”utf-8″‘}); res.end();
}).listen(8888); var socket =.io.listen(server);
socket.sockets.on(‘connection’, function(socket) { socket.emit(‘xxx’,
{options}); socket.on(‘xxx’, function(data) { // do someting }); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var http = require(‘http’);
var io = require(‘socket.io’);
 
var server = http.createServer(function(req, res) {
    res.writeHeader(200, {‘content-type’: ‘text/html;charset="utf-8"’});
    res.end();
}).listen(8888);
 
var socket =.io.listen(server);
 
socket.sockets.on(‘connection’, function(socket) {
    socket.emit(‘xxx’, {options});
 
    socket.on(‘xxx’, function(data) {
        // do someting
    });
});

深信明白websocket的同学不可以不晓得socket.io,因为socket.io太出名了,也很棒,它本身对逾期、握手等都做了拍卖。我猜忌那也是兑现websocket使用最多的法子。socket.io最最最突出的少数就是优雅降级,当浏览器不支持websocket时,它会在内部优雅降级为长轮询等,用户和开发者是不须要关心具体贯彻的,很有益于。

唯独事情是有两面性的,socket.io因为它的应有尽有也带动了坑的地点,最要紧的就是臃肿,它的包裹也给多少带动了较多的简报冗余,而且优雅降级这一独到之处,也伴随浏览器标准化的举办逐步失去了高大

Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+

在此间不是指责说socket.io不佳,已经被淘汰了,而是有时候我们也得以考虑部分此外的兑现~

 

2、http模块

刚巧说了socket.io臃肿,那现在就来说说便捷的,首先demo

JavaScript

var http = require(‘http’); var server = http.createServer();
server.on(‘upgrade’, function(req) { console.log(req.headers); });
server.listen(8888);

1
2
3
4
5
6
var http = require(‘http’);
var server = http.createServer();
server.on(‘upgrade’, function(req) {
console.log(req.headers);
});
server.listen(8888);

很简短的完结,其实socket.io内部对websocket也是那样完成的,然而前面帮大家封装了一部分handle处理,那里大家也得以团结去充足,给出两张socket.io中的源码图

亚洲必赢官网 1

亚洲必赢官网 2

 

3、ws模块

背后有个例证会用到,那里就提一下,前边具体看~

 

二、自己完成一套server端websocket

碰巧说了三种常见的websocket完成方式,现在我们想想,对于开发者来说

websocket相对于传统http数据交互格局以来,伸张了服务器推送的事件,客户端接收到事件再开展对应处理,开发起来分化并不是太大啊

那是因为那些模块已经帮我们将多少帧解析那边的坑都填好了,第二有些大家将尝试自己成立一套简便的服务器端websocket模块

谢谢次碳酸钴的琢磨支持,本身在此地那有的只是简短说下,假若对此有趣味好奇的请百度【web技术琢磨所】

自己姣好服务器端websocket首要有两点,一个是运用net模块接受数据流,还有一个是对照官方的帧结构图解析数据,完毕这两有的就已经完毕了全方位的底层工作

先是给一个客户端发送websocket握手报文的抓包内容

客户端代码很粗略

JavaScript

ws = new WebSocket(“ws://127.0.0.1:8888”);

1
ws = new WebSocket("ws://127.0.0.1:8888");

亚洲必赢官网 3

服务器端要针对那一个key验证,就是讲key加上一个一定的字符串后做四回sha1运算,将其结果转换为base64送回去

JavaScript

var crypto = require(‘crypto’); var WS =
‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’;
require(‘net’).createServer(function(o) { var key;
o.on(‘data’,function(e) { if(!key) { // 获取发送过来的KEY key =
e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; //
连接上WS这么些字符串,并做一遍sha1运算,最终转换成Base64 key =
crypto.createHash(‘sha1’).update(key+WS).digest(‘base64’); //
输出再次来到给客户端的数据,那个字段都是必须的 o.write(‘HTTP/1.1 101
Switching Protocols\r\n’); o.write(‘Upgrade: websocket\r\n’);
o.write(‘Connection: Upgrade\r\n’); // 这一个字段带上服务器处理后的KEY
o.write(‘Sec-WebSocket-Accept: ‘+key+’\r\n’); //
输出空行,使HTTP头甘休 o.write(‘\r\n’); } }); }).listen(8888);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var crypto = require(‘crypto’);
var WS = ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’;
 
require(‘net’).createServer(function(o) {
var key;
o.on(‘data’,function(e) {
if(!key) {
// 获取发送过来的KEY
key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
// 连接上WS这个字符串,并做一次sha1运算,最后转换成Base64
key = crypto.createHash(‘sha1’).update(key+WS).digest(‘base64’);
// 输出返回给客户端的数据,这些字段都是必须的
o.write(‘HTTP/1.1 101 Switching Protocols\r\n’);
o.write(‘Upgrade: websocket\r\n’);
o.write(‘Connection: Upgrade\r\n’);
// 这个字段带上服务器处理后的KEY
o.write(‘Sec-WebSocket-Accept: ‘+key+’\r\n’);
// 输出空行,使HTTP头结束
o.write(‘\r\n’);
}
});
}).listen(8888);

这般握手部分就已经落成了,后边就是数据帧解析与转变的活了

先看下官方提供的帧结构示意图

亚洲必赢官网 4

简简单单介绍下

FIN为是或不是停止的标志

RSV为预留空间,0

opcode标识数据类型,是不是分片,是不是二进制解析,心跳包等等

提交一张opcode对应图

亚洲必赢官网 5

MASK是不是使用掩码

Payload len和前边extend payload length表示数据长度,那个是最麻烦的

PayloadLen唯有7位,换成无符号整型的话唯有0到127的取值,这么小的数值当然无法描述较大的数码,由此确定当数码长度小于或等于125时候它才作为数据长度的讲述,倘使那些值为126,则时候背后的五个字节来存储数据长度,假诺为127则用后面七个字节来囤积数据长度

Masking-key掩码

下边贴出解析数据帧的代码

JavaScript

function decodeDataFrame(e) { var i = 0, j,s, frame = { FIN: e[i]
>> 7, Opcode: e[i++] & 15, Mask: e[i] >> 7,
PayloadLength: e[i++] & 0x7F }; if(frame.PayloadLength === 126) {
frame.PayloadLength = (e[i++] << 8) + e[i++]; }
if(frame.PayloadLength === 127) { i += 4; frame.PayloadLength =
(e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8)

  • e[i++]; } if(frame.Mask) { frame.MaskingKey = [e[i++], e[i++],
    e[i++], e[i++]]; for(j = 0, s = []; j < frame.PayloadLength;
    j++) { s.push(e[i+j] ^ frame.MaskingKey[j%4]); } } else { s =
    e.slice(i, i+frame.PayloadLength); } s = new Buffer(s); if(frame.Opcode
    === 1) { s = s.toString(); } frame.PayloadData = s; return frame; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function decodeDataFrame(e) {
var i = 0,
j,s,
frame = {
FIN: e[i] >> 7,
Opcode: e[i++] & 15,
Mask: e[i] >> 7,
PayloadLength: e[i++] & 0x7F
};
 
if(frame.PayloadLength === 126) {
frame.PayloadLength = (e[i++] << 8) + e[i++];
}
 
if(frame.PayloadLength === 127) {
i += 4;
frame.PayloadLength = (e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8) + e[i++];
}
 
if(frame.Mask) {
frame.MaskingKey = [e[i++], e[i++], e[i++], e[i++]];
 
for(j = 0, s = []; j < frame.PayloadLength; j++) {
s.push(e[i+j] ^ frame.MaskingKey[j%4]);
}
} else {
s = e.slice(i, i+frame.PayloadLength);
}
 
s = new Buffer(s);
 
if(frame.Opcode === 1) {
s = s.toString();
}
 
frame.PayloadData = s;
return frame;
}

下一场是生成数据帧的

JavaScript

function encodeDataFrame(e) { var s = [], o = new
Buffer(e.PayloadData), l = o.length; s.push((e.FIN << 7) +
e.Opcode); if(l < 126) { s.push(l); } else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0,
0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00)
>> 8, l&0xFF); } return Buffer.concat([new Buffer(s), o]); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function encodeDataFrame(e) {
var s = [],
o = new Buffer(e.PayloadData),
l = o.length;
 
s.push((e.FIN << 7) + e.Opcode);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), o]);
}

都是依照帧结构示意图上的去处理,在那里不细讲,文章首要在下有些,借使对那块感兴趣的话能够运动web技术研商所~

 

三、websocket传输图片和websocket语音聊天室

正片环节到了,那篇小说最重点的如故显得一下websocket的一部分运用情况

1、传输图片

俺们先考虑传输图片的手续是怎么着,首先服务器收到到客户端请求,然后读取图片文件,将二进制数据转载给客户端,客户端怎样处理?当然是应用File里德(Reade)r对象了

先给客户端代码

JavaScript

var ws = new WebSocket(“ws://xxx.xxx.xxx.xxx:8888”); ws.onopen =
function(){ console.log(“握手成功”); }; ws.onmessage = function(e) { var
reader = new FileReader(); reader.onload = function(event) { var
contents = event.target.result; var a = new Image(); a.src = contents;
document.body.appendChild(a); } reader.readAsDataURL(e.data); };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ws = new WebSocket("ws://xxx.xxx.xxx.xxx:8888");
 
ws.onopen = function(){
    console.log("握手成功");
};
 
ws.onmessage = function(e) {
    var reader = new FileReader();
    reader.onload = function(event) {
        var contents = event.target.result;
        var a = new Image();
        a.src = contents;
        document.body.appendChild(a);
    }
    reader.readAsDataURL(e.data);
};

收取到音讯,然后readAsDataURL,直接将图纸base64添加到页面中

转到服务器端代码

JavaScript

fs.readdir(“skyland”, function(err, files) { if(err) { throw err; }
for(var i = 0; i < files.length; i++) { fs.readFile(‘skyland/’ +
files[i], function(err, data) { if(err) { throw err; }
o.write(encodeImgFrame(data)); }); } }); function encodeImgFrame(buf) {
var s = [], l = buf.length, ret = []; s.push((1 << 7) + 2);
if(l < 126) { s.push(l); } else if(l < 0x10000) { s.push(126,
(l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0, 0, 0,
(l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00)
>> 8, l&0xFF); } return Buffer.concat([new Buffer(s), buf]); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fs.readdir("skyland", function(err, files) {
if(err) {
throw err;
}
for(var i = 0; i < files.length; i++) {
fs.readFile(‘skyland/’ + files[i], function(err, data) {
if(err) {
throw err;
}
 
o.write(encodeImgFrame(data));
});
}
});
 
function encodeImgFrame(buf) {
var s = [],
l = buf.length,
ret = [];
 
s.push((1 << 7) + 2);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), buf]);
}

注意s.push((1 << 7) +
2)
这一句,那里格外直接把opcode写死了为2,对于Binary
Frame,这样客户端接收到数量是不会尝试举行toString的,否则会报错~

代码很不难,在那里向我们享用一下websocket传输图片的快慢怎么样

测试很多张图纸,总共8.24M

常常静态资源服务器要求20s左右(服务器较远)

cdn需要2.8s左右

那大家的websocket方式吗??!

答案是均等要求20s左右,是或不是很失望……速度就是慢在传输上,并不是服务器读取图片,本机上一样的图片资源,1s左右方可做到……那样看来数据流也无法冲破距离的限量提升传输速度

下边我们来探视websocket的另一个用法~

 

用websocket搭建语音聊天室

先来收拾一下口音聊天室的功能

用户进入频道随后从Mike风输入音频,然后发送给后台转载给频道里面的其余人,其外人接收到信息举行广播

看起来困难在多个地方,第四个是节奏的输入,第二是吸收到数码流举行播放

先说音频的输入,那里运用了HTML5的getUserMedia方法,但是注意了,那个方法上线是有大坑的,最终说,先贴代码

JavaScript

if (navigator.getUserMedia) { navigator.getUserMedia( { audio: true },
function (stream) { var rec = new SRecorder(stream); recorder = rec; })
}

1
2
3
4
5
6
7
8
if (navigator.getUserMedia) {
    navigator.getUserMedia(
        { audio: true },
        function (stream) {
            var rec = new SRecorder(stream);
            recorder = rec;
        })
}

首先个参数是{audio:
true},只启用音频,然后创建了一个SRecorder对象,后续的操作基本上都在这一个目标上展开。此时倘诺代码运行在地面的话浏览器应该升迁您是不是启用迈克风输入,确定今后就开动了

接下去大家看下SRecorder构造函数是吗,给出首要的一些

JavaScript

var SRecorder = function(stream) { …… var context = new AudioContext();
var audioInput = context.createMediaStreamSource(stream); var recorder =
context.createScriptProcessor(4096, 1, 1); …… }

1
2
3
4
5
6
7
var SRecorder = function(stream) {
    ……
   var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
    ……
}

奥迪oContext是一个旋律上下文对象,有做过声音过滤处理的同班应该掌握“一段音频到达扬声器进行广播在此之前,半路对其开展拦阻,于是大家就获取了旋律数据了,那一个拦截工作是由window.奥迪oContext来做的,大家拥有对旋律的操作都根据那个目的”,我们得以经过奥迪(Audi)oContext成立不一致的奥迪(Audi)oNode节点,然后添加滤镜播放特其余音响

录音原理一样,大家也急需走奥迪oContext,不过多了一步对Mike风音频输入的吸纳上,而不是像以前处理音频一下用ajax请求音频的ArrayBuffer对象再decode,迈克风的收受须要用到createMediaStreamSource方法,注意那一个参数就是getUserMedia方法第三个参数的参数

况且createScriptProcessor方法,它官方的解释是:

Creates a ScriptProcessorNode, which can be used for direct audio
processing via JavaScript.

——————

概括下就是以此办法是选取JavaScript去处理音频采集操作

到头来到点子采集了!胜利就在面前!

接下去让我们把话筒的输入和音频采集相连起来

JavaScript

audioInput.connect(recorder); recorder.connect(context.destination);

1
2
audioInput.connect(recorder);
recorder.connect(context.destination);

context.destination官方表达如下

The destination property of
the AudioContext interface
returns
an AudioDestinationNoderepresenting
the final destination of all audio in the context.

——————

context.destination重临代表在条件中的音频的尾声目标地。

好,到了此时,大家还须求一个监听音频采集的风云

JavaScript

recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0)); }

1
2
3
recorder.onaudioprocess = function (e) {
    audioData.input(e.inputBuffer.getChannelData(0));
}

audioData是一个对象,那个是在网上找的,我就加了一个clear方法因为背后会用到,主要有万分encodeWAV方法很赞,外人进行了频仍的点子压缩和优化,那么些最终会陪伴完整的代码一起贴出来

这儿整体用户进入频道随后从Mike风输入音频环节就早已做到啦,上面就该是向劳动器端发送音频流,稍微有点蛋疼的来了,刚才大家说了,websocket通过opcode分歧可以象征回去的数量是文件照旧二进制数据,而我辈onaudioprocess中input进去的是数组,最后播放音响需求的是Blob,{type:
‘audio/wav’}的目标,那样我们就不可能不要在发送从前将数组转换成WAV的Blob,此时就用到了上边说的encodeWAV方法

服务器就像很不难,只要转载就行了

地面测试确实可以,但是天坑来了!将次第跑在服务器上时候调用getUserMedia方法提示我必须在一个有惊无险的条件,也就是须要https,那表示ws也务必换成wss……从而服务器代码就从不行使大家温馨包装的握手、解析和编码了,代码如下

JavaScript

var https = require(‘https’); var fs = require(‘fs’); var ws =
require(‘ws’); var userMap = Object.create(null); var options = { key:
fs.readFileSync(‘./privatekey.pem’), cert:
fs.readFileSync(‘./certificate.pem’) }; var server =
https.createServer(options, function(req, res) { res.writeHead({
‘Content-Type’ : ‘text/html’ }); fs.readFile(‘./testaudio.html’,
function(err, data) { if(err) { return ; } res.end(data); }); }); var
wss = new ws.Server({server: server}); wss.on(‘connection’, function(o)
{ o.on(‘message’, function(message) { if(message.indexOf(‘user’) === 0)
{ var user = message.split(‘:’)[1]; userMap[user] = o; } else {
for(var u in userMap) { userMap[u].send(message); } } }); });
server.listen(8888);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var https = require(‘https’);
var fs = require(‘fs’);
var ws = require(‘ws’);
var userMap = Object.create(null);
var options = {
    key: fs.readFileSync(‘./privatekey.pem’),
    cert: fs.readFileSync(‘./certificate.pem’)
};
var server = https.createServer(options, function(req, res) {
    res.writeHead({
        ‘Content-Type’ : ‘text/html’
    });
 
    fs.readFile(‘./testaudio.html’, function(err, data) {
        if(err) {
            return ;
        }
 
        res.end(data);
    });
});
 
var wss = new ws.Server({server: server});
 
wss.on(‘connection’, function(o) {
    o.on(‘message’, function(message) {
if(message.indexOf(‘user’) === 0) {
    var user = message.split(‘:’)[1];
    userMap[user] = o;
} else {
    for(var u in userMap) {
userMap[u].send(message);
    }
}
    });
});
 
server.listen(8888);

代码仍旧很简短的,使用https模块,然后用了初始说的ws模块,userMap是人云亦云的频段,只兑现转载的中央效率

行使ws模块是因为它突出https达成wss实在是太有利了,和逻辑代码0争论

https的搭建在此地就不提了,紧即使急需私钥、CSR证书签名和证件文件,感兴趣的同室可以领悟下(不过不打听的话在现网环境也用持续getUserMedia……)

下边是完好的前端代码

JavaScript

var a = document.getElementById(‘a’); var b =
document.getElementById(‘b’); var c = document.getElementById(‘c’);
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia; var gRecorder = null; var audio =
document.querySelector(‘audio’); var door = false; var ws = null;
b.onclick = function() { if(a.value === ”) { alert(‘请输入用户名’);
return false; } if(!navigator.getUserMedia) {
alert(‘抱歉您的配备无西班牙语音聊天’); return false; }
SRecorder.get(function (rec) { gRecorder = rec; }); ws = new
WebSocket(“wss://x.x.x.x:8888”); ws.onopen = function() {
console.log(‘握手成功’); ws.send(‘user:’ + a.value); }; ws.onmessage =
function(e) { receive(e.data); }; document.onkeydown = function(e) {
if(e.keyCode === 65) { if(!door) { gRecorder.start(); door = true; } }
}; document.onkeyup = function(e) { if(e.keyCode === 65) { if(door) {
ws.send(gRecorder.getBlob()); gRecorder.clear(); gRecorder.stop(); door
= false; } } } } c.onclick = function() { if(ws) { ws.close(); } } var
SRecorder = function(stream) { config = {}; config.sampleBits =
config.smapleBits || 8; config.sampleRate = config.sampleRate || (44100
/ 6); var context = new 奥迪oContext(); var audioInput =
context.createMediaStreamSource(stream); var recorder =
context.createScriptProcessor(4096, 1, 1); var audioData = { size: 0
//录音文件长度 , buffer: [] //录音缓存 , input萨姆(Sam)pleRate:
context.sampleRate //输入采样率 , input萨姆pleBits: 16 //输入采样数位 8,
16 , output山姆pleRate: config.sampleRate //输出采样率 , outut山姆pleBits:
config.sampleBits //输出采样数位 8, 16 , clear: function() { this.buffer
= []; this.size = 0; } , input: function (data) { this.buffer.push(new
Float32Array(data)); this.size += data.length; } , compress: function ()
{ //合并压缩 //合并 var data = new Float32Array(this.size); var offset =
0; for (var i = 0; i < this.buffer.length; i++) {
data.set(this.buffer[i], offset); offset += this.buffer[i].length; }
//压缩 var compression = parseInt(this.inputSampleRate /
this.outputSampleRate); var length = data.length / compression; var
result = new Float32Array(length); var index = 0, j = 0; while (index
< length) { result[index] = data[j]; j += compression; index++; }
return result; } , encodeWAV: function () { var sampleRate =
Math.min(this.inputSampleRate, this.outputSampleRate); var sampleBits =
Math.min(this.inputSampleBits, this.oututSampleBits); var bytes =
this.compress(); var dataLength = bytes.length * (sampleBits / 8); var
buffer = new ArrayBuffer(44 + dataLength); var data = new
DataView(buffer); var channelCount = 1;//单声道 var offset = 0; var
writeString = function (str) { for (var i = 0; i < str.length; i++) {
data.setUint8(offset + i, str.charCodeAt(i)); } }; // 资源沟通文件标识符
writeString(‘RIFF’); offset += 4; //
下个地方开头到文件尾总字节数,即文件大小-8 data.setUint32(offset, 36 +
dataLength, true); offset += 4; // WAV文件申明 writeString(‘WAVE’);
offset += 4; // 波形格式标志 writeString(‘fmt ‘); offset += 4; //
过滤字节,一般为 0x10 = 16 data.setUint32(offset, 16, true); offset += 4;
// 格式种类 (PCM方式采样数据) data.setUint16(offset, 1, true); offset +=
2; // 通道数 data.setUint16(offset, channelCount, true); offset += 2; //
采样率,每秒样本数,表示每个通道的播报速度 data.setUint32(offset,
sampleRate, true); offset += 4; // 波形数据传输率 (每秒平均字节数)
单声道×每秒数据位数×每样本数据位/8 data.setUint32(offset, channelCount
* sampleRate * (sampleBits / 8), true); offset += 4; // 快数据调整数
采样一回占用字节数 单声道×每样本的数目位数/8 data.setUint16(offset,
channelCount * (sampleBits / 8), true); offset += 2; // 每样本数量位数
data.setUint16(offset, sampleBits, true); offset += 2; // 数据标识符
writeString(‘data’); offset += 4; // 采样数据总数,即数据总大小-44
data.setUint32(offset, dataLength, true); offset += 4; // 写入采样数据
if (sampleBits === 8) { for (var i = 0; i < bytes.length; i++,
offset++) { var s = Math.max(-1, Math.min(1, bytes[i])); var val = s
< 0 ? s * 0x8000 : s * 0x7FFF; val = parseInt(255 / (65535 / (val +
32768))); data.setInt8(offset, val, true); } } else { for (var i = 0; i
< bytes.length; i++, offset += 2) { var s = Math.max(-1, Math.min(1,
bytes[i])); data.setInt16(offset, s < 0 ? s * 0x8000 : s *
0x7FFF, true); } } return new Blob([data], { type: ‘audio/wav’ }); }
}; this.start = function () { audioInput.connect(recorder);
recorder.connect(context.destination); } this.stop = function () {
recorder.disconnect(); } this.getBlob = function () { return
audioData.encodeWAV(); } this.clear = function() { audioData.clear(); }
recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0)); } }; SRecorder.get =
function (callback) { if (callback) { if (navigator.getUserMedia) {
navigator.getUserMedia( { audio: true }, function (stream) { var rec =
new SRecorder(stream); callback(rec); }) } } } function receive(e) {
audio.src = window.URL.createObjectURL(e); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
var a = document.getElementById(‘a’);
var b = document.getElementById(‘b’);
var c = document.getElementById(‘c’);
 
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
 
var gRecorder = null;
var audio = document.querySelector(‘audio’);
var door = false;
var ws = null;
 
b.onclick = function() {
    if(a.value === ”) {
        alert(‘请输入用户名’);
        return false;
    }
    if(!navigator.getUserMedia) {
        alert(‘抱歉您的设备无法语音聊天’);
        return false;
    }
 
    SRecorder.get(function (rec) {
        gRecorder = rec;
    });
 
    ws = new WebSocket("wss://x.x.x.x:8888");
 
    ws.onopen = function() {
        console.log(‘握手成功’);
        ws.send(‘user:’ + a.value);
    };
 
    ws.onmessage = function(e) {
        receive(e.data);
    };
 
    document.onkeydown = function(e) {
        if(e.keyCode === 65) {
            if(!door) {
                gRecorder.start();
                door = true;
            }
        }
    };
 
    document.onkeyup = function(e) {
        if(e.keyCode === 65) {
            if(door) {
                ws.send(gRecorder.getBlob());
                gRecorder.clear();
                gRecorder.stop();
                door = false;
            }
        }
    }
}
 
c.onclick = function() {
    if(ws) {
        ws.close();
    }
}
 
var SRecorder = function(stream) {
    config = {};
 
    config.sampleBits = config.smapleBits || 8;
    config.sampleRate = config.sampleRate || (44100 / 6);
 
    var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
 
    var audioData = {
        size: 0          //录音文件长度
        , buffer: []     //录音缓存
        , inputSampleRate: context.sampleRate    //输入采样率
        , inputSampleBits: 16       //输入采样数位 8, 16
        , outputSampleRate: config.sampleRate    //输出采样率
        , oututSampleBits: config.sampleBits       //输出采样数位 8, 16
        , clear: function() {
            this.buffer = [];
            this.size = 0;
        }
        , input: function (data) {
            this.buffer.push(new Float32Array(data));
            this.size += data.length;
        }
        , compress: function () { //合并压缩
            //合并
            var data = new Float32Array(this.size);
            var offset = 0;
            for (var i = 0; i < this.buffer.length; i++) {
                data.set(this.buffer[i], offset);
                offset += this.buffer[i].length;
            }
            //压缩
            var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
            var length = data.length / compression;
            var result = new Float32Array(length);
            var index = 0, j = 0;
            while (index < length) {
                result[index] = data[j];
                j += compression;
                index++;
            }
            return result;
        }
        , encodeWAV: function () {
            var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
            var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
            var bytes = this.compress();
            var dataLength = bytes.length * (sampleBits / 8);
            var buffer = new ArrayBuffer(44 + dataLength);
            var data = new DataView(buffer);
 
            var channelCount = 1;//单声道
            var offset = 0;
 
            var writeString = function (str) {
                for (var i = 0; i < str.length; i++) {
                    data.setUint8(offset + i, str.charCodeAt(i));
                }
            };
 
            // 资源交换文件标识符
            writeString(‘RIFF’); offset += 4;
            // 下个地址开始到文件尾总字节数,即文件大小-8
            data.setUint32(offset, 36 + dataLength, true); offset += 4;
            // WAV文件标志
            writeString(‘WAVE’); offset += 4;
            // 波形格式标志
            writeString(‘fmt ‘); offset += 4;
            // 过滤字节,一般为 0x10 = 16
            data.setUint32(offset, 16, true); offset += 4;
            // 格式类别 (PCM形式采样数据)
            data.setUint16(offset, 1, true); offset += 2;
            // 通道数
            data.setUint16(offset, channelCount, true); offset += 2;
            // 采样率,每秒样本数,表示每个通道的播放速度
            data.setUint32(offset, sampleRate, true); offset += 4;
            // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
            data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
            // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
            data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
            // 每样本数据位数
            data.setUint16(offset, sampleBits, true); offset += 2;
            // 数据标识符
            writeString(‘data’); offset += 4;
            // 采样数据总数,即数据总大小-44
            data.setUint32(offset, dataLength, true); offset += 4;
            // 写入采样数据
            if (sampleBits === 8) {
                for (var i = 0; i < bytes.length; i++, offset++) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
                    val = parseInt(255 / (65535 / (val + 32768)));
                    data.setInt8(offset, val, true);
                }
            } else {
                for (var i = 0; i < bytes.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
            }
 
            return new Blob([data], { type: ‘audio/wav’ });
        }
    };
 
    this.start = function () {
        audioInput.connect(recorder);
        recorder.connect(context.destination);
    }
 
    this.stop = function () {
        recorder.disconnect();
    }
 
    this.getBlob = function () {
        return audioData.encodeWAV();
    }
 
    this.clear = function() {
        audioData.clear();
    }
 
    recorder.onaudioprocess = function (e) {
        audioData.input(e.inputBuffer.getChannelData(0));
    }
};
 
SRecorder.get = function (callback) {
    if (callback) {
        if (navigator.getUserMedia) {
            navigator.getUserMedia(
                { audio: true },
                function (stream) {
                    var rec = new SRecorder(stream);
                    callback(rec);
                })
        }
    }
}
 
function receive(e) {
    audio.src = window.URL.createObjectURL(e);
}

注意:按住a键说话,放开a键发送

友善有品味不按键实时对讲,通过setInterval发送,但意识杂音有点重,效果不佳,那一个必要encodeWAV再一层的卷入,多去除环境杂音的法力,自己挑选了更进一步便民的按键说话的情势

 

那篇文章里首先展望了websocket的将来,然后按照专业大家团结一心尝试解析和变化数据帧,对websocket有了更深一步的询问

终极通过八个demo看到了websocket的潜力,关于语音聊天室的demo涉及的较广,没有接触过奥迪(Audi)oContext对象的同室最好先明白下奥迪oContext

小说到那边就与世长辞啦~有怎么样想法和问题欢迎我们提议来一起探讨探索~

 

1 赞 11 收藏 3
评论

亚洲必赢官网 6

原文出处:
AlloyTeam   

原稿出处:
AlloyTeam   

原文出处:
AlloyTeam   

说到websocket想比大家不会陌生,即使陌生的话也没涉及,一句话概括

说到websocket想比我们不会陌生,如果陌生的话也没涉及,一句话概括

说到websocket想比大家不会陌生,假如陌生的话也没涉及,一句话概括

“WebSocket protocol
是HTML5一种新的商事。它已毕了浏览器与服务器全双工通讯”

“WebSocket protocol
是HTML5一种新的商谈。它落成了浏览器与服务器全双工通讯”

图形的力量,websocket探索其与语音。“WebSocket protocol
是HTML5一种新的商事。它完毕了浏览器与服务器全双工通讯”

WebSocket相比较传统那么些服务器推技术大概好了太多,大家可以挥手向comet和长轮询那个技巧说拜拜啦,庆幸大家生活在有着HTML5的时代~

WebSocket相比较传统这一个服务器推技术几乎好了太多,大家可以挥手向comet和长轮询这几个技能说拜拜啦,庆幸大家生活在具备HTML5的一时~

WebSocket相比较传统那个服务器推技术大致好了太多,我们可以挥手向comet和长轮询那一个技巧说拜拜啦,庆幸大家生存在享有HTML5的一代~

这篇小说我们将分三部分探索websocket

那篇文章我们将分三有的探索websocket

这篇文章大家将分三片段探索websocket

率先是websocket的普遍使用,其次是一点一滴自己打造服务器端websocket,最后是任重先生而道远介绍利用websocket制作的五个demo,传输图片和在线语音聊天室,let’s
go

首先是websocket的广阔使用,其次是完全自己创立服务器端websocket,最后是根本介绍利用websocket制作的多少个demo,传输图片和在线语音聊天室,let’s
go

第一是websocket的科普使用,其次是全然自己制作服务器端websocket,最终是至关主要介绍利用websocket制作的多个demo,传输图片和在线语音聊天室,let’s
go

一、websocket常见用法

一、websocket常见用法

一、websocket常见用法

那里介绍三种自我以为大规模的websocket落成……(只顾:本文建立在node上下文环境

那里介绍三种自己觉得大规模的websocket落成……(留神:本文建立在node上下文环境

那里介绍三种自己觉着大规模的websocket落成……(留意:本文建立在node上下文环境

1、socket.io

1、socket.io

1、socket.io

先给demo

先给demo

先给demo

JavaScript

JavaScript

JavaScript

var http = require(‘http’); var io = require(‘socket.io’); var server =
http.createServer(function(req, res) { res.writeHeader(200,
{‘content-type’: ‘text/html;charset=”utf-8″‘}); res.end();
}).listen(8888); var socket =.io.listen(server);
socket.sockets.on(‘connection’, function(socket) { socket.emit(‘xxx’,
{options}); socket.on(‘xxx’, function(data) { // do someting }); });

var http = require(‘http’); var io = require(‘socket.io’); var server =
http.createServer(function(req, res) { res.writeHeader(200,
{‘content-type’: ‘text/html;charset=”utf-8″‘}); res.end();
}).listen(8888); var socket =.io.listen(server);
socket.sockets.on(‘connection’, function(socket) { socket.emit(‘xxx’,
{options}); socket.on(‘xxx’, function(data) { // do someting }); });

var http = require(‘http’); var io = require(‘socket.io’); var server =
http.createServer(function(req, res) { res.writeHeader(200,
{‘content-type’: ‘text/html;charset=”utf-8″‘}); res.end();
}).listen(8888); var socket =.io.listen(server);
socket.sockets.on(‘connection’, function(socket) { socket.emit(‘xxx’,
{options}); socket.on(‘xxx’, function(data) { // do someting }); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var http = require(‘http’);
var io = require(‘socket.io’);
 
var server = http.createServer(function(req, res) {
    res.writeHeader(200, {‘content-type’: ‘text/html;charset="utf-8"’});
    res.end();
}).listen(8888);
 
var socket =.io.listen(server);
 
socket.sockets.on(‘connection’, function(socket) {
    socket.emit(‘xxx’, {options});
 
    socket.on(‘xxx’, function(data) {
        // do someting
    });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var http = require(‘http’);
var io = require(‘socket.io’);
 
var server = http.createServer(function(req, res) {
    res.writeHeader(200, {‘content-type’: ‘text/html;charset="utf-8"’});
    res.end();
}).listen(8888);
 
var socket =.io.listen(server);
 
socket.sockets.on(‘connection’, function(socket) {
    socket.emit(‘xxx’, {options});
 
    socket.on(‘xxx’, function(data) {
        // do someting
    });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var http = require(‘http’);
var io = require(‘socket.io’);
 
var server = http.createServer(function(req, res) {
    res.writeHeader(200, {‘content-type’: ‘text/html;charset="utf-8"’});
    res.end();
}).listen(8888);
 
var socket =.io.listen(server);
 
socket.sockets.on(‘connection’, function(socket) {
    socket.emit(‘xxx’, {options});
 
    socket.on(‘xxx’, function(data) {
        // do someting
    });
});

深信不疑精晓websocket的同班不能不明白socket.io,因为socket.io太盛名了,也很棒,它本身对过期、握手等都做了处理。我估算那也是兑现websocket使用最多的章程。socket.io最最最地道的某些就是优雅降级,当浏览器不援助websocket时,它会在中间优雅降级为长轮询等,用户和开发者是不需要关切具体落到实处的,很便利。

信任领悟websocket的同班不可以不了解socket.io,因为socket.io太盛名了,也很棒,它自身对过期、握手等都做了处理。我猜想那也是完毕websocket使用最多的不二法门。socket.io最最最美妙的一点就是优雅降级,当浏览器不接济websocket时,它会在中间优雅降级为长轮询等,用户和开发者是不必要关注具体贯彻的,很便宜。

深信不疑通晓websocket的校友无法不领会socket.io,因为socket.io太知名了,也很棒,它自己对过期、握手等都做了处理。我困惑那也是已毕websocket使用最多的法子。socket.io最最最美好的某些就是优雅降级,当浏览器不协理websocket时,它会在中间优雅降级为长轮询等,用户和开发者是不必要关注具体完成的,很有利。

只是事情是有两面性的,socket.io因为它的通盘也带动了坑的地点,最要紧的就是臃肿,它的卷入也给多少带动了较多的简报冗余,而且优雅降级这一优点,也陪伴浏览器标准化的拓展逐步失去了英雄

然则事情是有两面性的,socket.io因为它的不偏不倚也带来了坑的地点,最重大的就是臃肿,它的卷入也给多少带动了较多的广播发表冗余,而且优雅降级这一独到之处,也陪同浏览器标准化的拓展逐步失去了宏伟

而是工作是有两面性的,socket.io因为它的周密也带动了坑的地点,最重点的就是臃肿,它的包裹也给多少带动了较多的简报冗余,而且优雅降级这一亮点,也伴随浏览器标准化的展开逐步失去了宏伟

Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+
Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+
Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+

在此地不是指责说socket.io不好,已经被淘汰了,而是有时候我们也得以设想部分其余的兑现~

在这边不是指责说socket.io糟糕,已经被淘汰了,而是有时候大家也可以考虑部分其余的兑现~

在那边不是指责说socket.io不好,已经被淘汰了,而是有时候大家也得以考虑部分别样的贯彻~

 

 

 

2、http模块

2、http模块

2、http模块

碰巧说了socket.io臃肿,那现在就来说说便捷的,首先demo

恰好说了socket.io臃肿,那现在就来说说便捷的,首先demo

恰恰说了socket.io臃肿,那现在就来说说便捷的,首先demo

JavaScript

JavaScript

JavaScript

var http = require(‘http’); var server = http.createServer();
server.on(‘upgrade’, function(req) { console.log(req.headers); });
server.listen(8888);

var http = require(‘http’); var server = http.createServer();
server.on(‘upgrade’, function(req) { console.log(req.headers); });
server.listen(8888);

var http = require(‘http’); var server = http.createServer();
server.on(‘upgrade’, function(req) { console.log(req.headers); });
server.listen(8888);

1
2
3
4
5
6
var http = require(‘http’);
var server = http.createServer();
server.on(‘upgrade’, function(req) {
console.log(req.headers);
});
server.listen(8888);
1
2
3
4
5
6
var http = require(‘http’);
var server = http.createServer();
server.on(‘upgrade’, function(req) {
console.log(req.headers);
});
server.listen(8888);
1
2
3
4
5
6
var http = require(‘http’);
var server = http.createServer();
server.on(‘upgrade’, function(req) {
console.log(req.headers);
});
server.listen(8888);

很粗略的贯彻,其实socket.io内部对websocket也是那般落成的,不过前面帮我们封装了有的handle处理,那里大家也足以友善去丰盛,给出两张socket.io中的源码图

很不难的兑现,其实socket.io内部对websocket也是如此落成的,可是前边帮大家封装了一些handle处理,这里我们也可以协调去丰富,给出两张socket.io中的源码图

很简短的兑现,其实socket.io内部对websocket也是这么完结的,然而前面帮大家封装了有些handle处理,那里大家也得以自己去丰盛,给出两张socket.io中的源码图

亚洲必赢官网 7

亚洲必赢官网 8

亚洲必赢官网 9

亚洲必赢官网 10

亚洲必赢官网 11

亚洲必赢官网 12

 

 

 

3、ws模块

亚洲必赢官网,3、ws模块

3、ws模块

末端有个例子会用到,那里就提一下,前边具体看~

背后有个例证会用到,那里就提一下,前面具体看~

后边有个例证会用到,那里就提一下,前边具体看~

 

 

 

二、自己完毕一套server端websocket

二、自己完结一套server端websocket

二、自己落成一套server端websocket

恰恰说了三种普遍的websocket完成方式,现在我们考虑,对于开发者来说

恰巧说了二种常见的websocket完成方式,现在大家思考,对于开发者来说

碰巧说了三种常见的websocket完结形式,现在大家想想,对于开发者来说

websocket相对于传统http数据交互情势以来,增添了服务器推送的轩然大波,客户端接收到事件再拓展对应处理,开发起来不相同并不是太大啊

websocket相对于传统http数据交互形式以来,伸张了服务器推送的事件,客户端接收到事件再展开相应处理,开发起来分裂并不是太大呀

websocket相对于传统http数据交互方式以来,扩张了服务器推送的事件,客户端接收到事件再举行相应处理,开发起来不相同并不是太大呀

那是因为这几个模块已经帮大家将数码帧解析此处的坑都填好了,第二部分我们将尝试自己创建一套简便的服务器端websocket模块

那是因为那个模块已经帮我们将数量帧解析那边的坑都填好了,第二有的我们将尝试自己打造一套简便的服务器端websocket模块

那是因为那多少个模块已经帮大家将数量帧解析此间的坑都填好了,第二片段大家将尝试自己成立一套简便的服务器端websocket模块

谢谢次碳酸钴的研商帮忙,自家在此处这部分只是简单说下,假设对此有趣味好奇的请百度【web技术琢磨所】

谢谢次碳酸钴的啄磨支持,自己在那边那有的只是简单说下,要是对此有趣味好奇的请百度【web技术商量所】

谢谢次碳酸钴的研商帮忙,我在那边那部分只是简单说下,如若对此有趣味好奇的请百度【web技术研究所】

和谐成功服务器端websocket主要有两点,一个是行使net模块接受数据流,还有一个是比照官方的帧结构图解析数据,落成那两片段就已经形成了总体的底部工作

温馨做到服务器端websocket主要有两点,一个是行使net模块接受数据流,还有一个是相比较官方的帧结构图解析数据,完结那两有些就曾经形成了一切的平底工作

团结已毕服务器端websocket首要有两点,一个是行使net模块接受数据流,还有一个是对待官方的帧结构图解析数据,达成那两局地就早已做到了上上下下的最底层工作

首先给一个客户端发送websocket握手报文的抓包内容

第一给一个客户端发送websocket握手报文的抓包内容

首先给一个客户端发送websocket握手报文的抓包内容

客户端代码很不难

客户端代码很简短

客户端代码很粗略

JavaScript

JavaScript

JavaScript

ws = new WebSocket(“ws://127.0.0.1:8888”);

ws = new WebSocket(“ws://127.0.0.1:8888”);

ws = new WebSocket(“ws://127.0.0.1:8888”);

1
ws = new WebSocket("ws://127.0.0.1:8888");
1
ws = new WebSocket("ws://127.0.0.1:8888");
1
ws = new WebSocket("ws://127.0.0.1:8888");

亚洲必赢官网 13

亚洲必赢官网 14

亚洲必赢官网 15

劳务器端要针对性那一个key验证,就是讲key加上一个一定的字符串后做四次sha1运算,将其结果转换为base64送回去

服务器端要针对性那些key验证,就是讲key加上一个特定的字符串后做三回sha1运算,将其结果转换为base64送重回

劳动器端要针对性这几个key验证,就是讲key加上一个一定的字符串后做四遍sha1运算,将其结果转换为base64送回来

JavaScript

JavaScript

JavaScript

var crypto = require(‘crypto’); var WS =
‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’;
require(‘net’).createServer(function(o) { var key;
o.on(‘data’,function(e) { if(!key) { // 获取发送过来的KEY key =
e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; //
连接上WS那些字符串,并做三遍sha1运算,最后转换成Base64 key =
crypto.createHash(‘sha1’).update(key+WS).digest(‘base64’); //
输出重回给客户端的数额,那一个字段都是必须的 o.write(‘HTTP/1.1 101
Switching Protocols\r\n’); o.write(‘Upgrade: websocket\r\n’);
o.write(‘Connection: Upgrade\r\n’); // 那个字段带上服务器处理后的KEY
o.write(‘Sec-WebSocket-Accept: ‘+key+’\r\n’); //
输出空行,使HTTP头为止 o.write(‘\r\n’); } }); }).listen(8888);

var crypto = require(‘crypto’); var WS =
‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’;
require(‘net’).createServer(function(o) { var key;
o.on(‘data’,function(e) { if(!key) { // 获取发送过来的KEY key =
e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; //
连接上WS那几个字符串,并做三遍sha1运算,最后转换成Base64 key =
crypto.createHash(‘sha1’).update(key+WS).digest(‘base64’); //
输出再次来到给客户端的数据,那些字段都是必须的 o.write(‘HTTP/1.1 101
Switching Protocols\r\n’); o.write(‘Upgrade: websocket\r\n’);
o.write(‘Connection: Upgrade\r\n’); // 那个字段带上服务器处理后的KEY
o.write(‘Sec-WebSocket-Accept: ‘+key+’\r\n’); //
输出空行,使HTTP头甘休 o.write(‘\r\n’); } }); }).listen(8888);

var crypto = require(‘crypto’); var WS =
‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’;
require(‘net’).createServer(function(o) { var key;
o.on(‘data’,function(e) { if(!key) { // 获取发送过来的KEY key =
e.toString().match(/Sec-WebSocket-Key: (.+)/)[1]; //
连接上WS这几个字符串,并做一遍sha1运算,最后转换成Base64 key =
crypto.createHash(‘sha1’).update(key+WS).digest(‘base64’); //
输出重回给客户端的数码,那几个字段都是必须的 o.write(‘HTTP/1.1 101
Switching Protocols\r\n’); o.write(‘Upgrade: websocket\r\n’);
o.write(‘Connection: Upgrade\r\n’); // 那些字段带上服务器处理后的KEY
o.write(‘Sec-WebSocket-Accept: ‘+key+’\r\n’); //
输出空行,使HTTP头为止 o.write(‘\r\n’); } }); }).listen(8888);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var crypto = require(‘crypto’);
var WS = ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’;
 
require(‘net’).createServer(function(o) {
var key;
o.on(‘data’,function(e) {
if(!key) {
// 获取发送过来的KEY
key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
// 连接上WS这个字符串,并做一次sha1运算,最后转换成Base64
key = crypto.createHash(‘sha1’).update(key+WS).digest(‘base64’);
// 输出返回给客户端的数据,这些字段都是必须的
o.write(‘HTTP/1.1 101 Switching Protocols\r\n’);
o.write(‘Upgrade: websocket\r\n’);
o.write(‘Connection: Upgrade\r\n’);
// 这个字段带上服务器处理后的KEY
o.write(‘Sec-WebSocket-Accept: ‘+key+’\r\n’);
// 输出空行,使HTTP头结束
o.write(‘\r\n’);
}
});
}).listen(8888);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var crypto = require(‘crypto’);
var WS = ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’;
 
require(‘net’).createServer(function(o) {
var key;
o.on(‘data’,function(e) {
if(!key) {
// 获取发送过来的KEY
key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
// 连接上WS这个字符串,并做一次sha1运算,最后转换成Base64
key = crypto.createHash(‘sha1’).update(key+WS).digest(‘base64’);
// 输出返回给客户端的数据,这些字段都是必须的
o.write(‘HTTP/1.1 101 Switching Protocols\r\n’);
o.write(‘Upgrade: websocket\r\n’);
o.write(‘Connection: Upgrade\r\n’);
// 这个字段带上服务器处理后的KEY
o.write(‘Sec-WebSocket-Accept: ‘+key+’\r\n’);
// 输出空行,使HTTP头结束
o.write(‘\r\n’);
}
});
}).listen(8888);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var crypto = require(‘crypto’);
var WS = ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’;
 
require(‘net’).createServer(function(o) {
var key;
o.on(‘data’,function(e) {
if(!key) {
// 获取发送过来的KEY
key = e.toString().match(/Sec-WebSocket-Key: (.+)/)[1];
// 连接上WS这个字符串,并做一次sha1运算,最后转换成Base64
key = crypto.createHash(‘sha1’).update(key+WS).digest(‘base64’);
// 输出返回给客户端的数据,这些字段都是必须的
o.write(‘HTTP/1.1 101 Switching Protocols\r\n’);
o.write(‘Upgrade: websocket\r\n’);
o.write(‘Connection: Upgrade\r\n’);
// 这个字段带上服务器处理后的KEY
o.write(‘Sec-WebSocket-Accept: ‘+key+’\r\n’);
// 输出空行,使HTTP头结束
o.write(‘\r\n’);
}
});
}).listen(8888);

如此握手部分就已经到位了,前面就是数据帧解析与转变的活了

那样握手部分就已经完毕了,前面就是数据帧解析与转变的活了

如此那般握手部分就曾经形成了,后边就是多少帧解析与转移的活了

先看下官方提供的帧结构示意图

先看下官方提供的帧结构示意图

先看下官方提供的帧结构示意图

亚洲必赢官网 16

亚洲必赢官网 17

亚洲必赢官网 18

简言之介绍下

简短介绍下

简易介绍下

FIN为是或不是终止的标记

FIN为是或不是得了的标志

FIN为是还是不是终止的标记

RSV为留下空间,0

RSV为留住空间,0

RSV为留下空间,0

opcode标识数据类型,是或不是分片,是不是二进制解析,心跳包等等

opcode标识数据类型,是还是不是分片,是还是不是二进制解析,心跳包等等

opcode标识数据类型,是还是不是分片,是还是不是二进制解析,心跳包等等

付出一张opcode对应图

交由一张opcode对应图

提交一张opcode对应图

亚洲必赢官网 19

亚洲必赢官网 20

亚洲必赢官网 21

MASK是不是采纳掩码

MASK是或不是利用掩码

MASK是不是拔取掩码

Payload len和后边extend payload length表示数据长度,这几个是最劳碌的

Payload len和后边extend payload length表示数据长度,那个是最辛勤的

Payload len和后边extend payload length表示数据长度,这些是最劳累的

PayloadLen唯有7位,换成无符号整型的话只有0到127的取值,这么小的数值当然无法描述较大的数据,由此确定当数码长度小于或等于125时候它才作为数据长度的讲述,若是那几个值为126,则时候背后的四个字节来储存数据长度,假若为127则用后边三个字节来存储数据长度

PayloadLen唯有7位,换成无符号整型的话只有0到127的取值,这么小的数值当然不可能描述较大的多少,由此确定当数码长度小于或等于125时候它才作为数据长度的讲述,假使那么些值为126,则时候背后的五个字节来囤积数据长度,如若为127则用前面三个字节来存储数据长度

PayloadLen只有7位,换成无符号整型的话只有0到127的取值,这么小的数值当然不可以描述较大的数码,因而确定当数码长度小于或等于125时候它才作为数据长度的讲述,假诺那一个值为126,则时候背后的多少个字节来储存数据长度,借使为127则用后边两个字节来存储数据长度

Masking-key掩码

Masking-key掩码

Masking-key掩码

上边贴出解析数据帧的代码

上面贴出解析数据帧的代码

上面贴出解析数据帧的代码

JavaScript

JavaScript

JavaScript

function decodeDataFrame(e) { var i = 0, j,s, frame = { FIN: e[i]
>> 7, Opcode: e[i++] & 15, Mask: e[i] >> 7,
PayloadLength: e[i++] & 0x7F }; if(frame.PayloadLength === 126) {
frame.PayloadLength = (e[i++] << 8) + e[i++]; }
if(frame.PayloadLength === 127) { i += 4; frame.PayloadLength =
(e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8)

function decodeDataFrame(e) { var i = 0, j,s, frame = { FIN: e[i]
>> 7, Opcode: e[i++] & 15, Mask: e[i] >> 7,
PayloadLength: e[i++] & 0x7F }; if(frame.PayloadLength === 126) {
frame.PayloadLength = (e[i++] << 8) + e[i++]; }
if(frame.PayloadLength === 127) { i += 4; frame.PayloadLength =
(e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8)

function decodeDataFrame(e) { var i = 0, j,s, frame = { FIN: e[i]
>> 7, Opcode: e[i++] & 15, Mask: e[i] >> 7,
PayloadLength: e[i++] & 0x7F }; if(frame.PayloadLength === 126) {
frame.PayloadLength = (e[i++] << 8) + e[i++]; }
if(frame.PayloadLength === 127) { i += 4; frame.PayloadLength =
(e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8)

  • e[i++]; } if(frame.Mask) { frame.MaskingKey = [e[i++], e[i++],
    e[i++], e[i++]]; for(j = 0, s = []; j < frame.PayloadLength;
    j++) { s.push(e[i+j] ^ frame.MaskingKey[j%4]); } } else { s =
    e.slice(i, i+frame.PayloadLength); } s = new Buffer(s); if(frame.Opcode
    === 1) { s = s.toString(); } frame.PayloadData = s; return frame; }
  • e[i++]; } if(frame.Mask) { frame.MaskingKey = [e[i++], e[i++],
    e[i++], e[i++]]; for(j = 0, s = []; j < frame.PayloadLength;
    j++) { s.push(e[i+j] ^ frame.MaskingKey[j%4]); } } else { s =
    e.slice(i, i+frame.PayloadLength); } s = new Buffer(s); if(frame.Opcode
    === 1) { s = s.toString(); } frame.PayloadData = s; return frame; }
  • e[i++]; } if(frame.Mask) { frame.MaskingKey = [e[i++], e[i++],
    e[i++], e[i++]]; for(j = 0, s = []; j < frame.PayloadLength;
    j++) { s.push(e[i+j] ^ frame.MaskingKey[j%4]); } } else { s =
    e.slice(i, i+frame.PayloadLength); } s = new Buffer(s); if(frame.Opcode
    === 1) { s = s.toString(); } frame.PayloadData = s; return frame; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function decodeDataFrame(e) {
var i = 0,
j,s,
frame = {
FIN: e[i] >> 7,
Opcode: e[i++] & 15,
Mask: e[i] >> 7,
PayloadLength: e[i++] & 0x7F
};
 
if(frame.PayloadLength === 126) {
frame.PayloadLength = (e[i++] << 8) + e[i++];
}
 
if(frame.PayloadLength === 127) {
i += 4;
frame.PayloadLength = (e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8) + e[i++];
}
 
if(frame.Mask) {
frame.MaskingKey = [e[i++], e[i++], e[i++], e[i++]];
 
for(j = 0, s = []; j < frame.PayloadLength; j++) {
s.push(e[i+j] ^ frame.MaskingKey[j%4]);
}
} else {
s = e.slice(i, i+frame.PayloadLength);
}
 
s = new Buffer(s);
 
if(frame.Opcode === 1) {
s = s.toString();
}
 
frame.PayloadData = s;
return frame;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function decodeDataFrame(e) {
var i = 0,
j,s,
frame = {
FIN: e[i] >> 7,
Opcode: e[i++] & 15,
Mask: e[i] >> 7,
PayloadLength: e[i++] & 0x7F
};
 
if(frame.PayloadLength === 126) {
frame.PayloadLength = (e[i++] << 8) + e[i++];
}
 
if(frame.PayloadLength === 127) {
i += 4;
frame.PayloadLength = (e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8) + e[i++];
}
 
if(frame.Mask) {
frame.MaskingKey = [e[i++], e[i++], e[i++], e[i++]];
 
for(j = 0, s = []; j < frame.PayloadLength; j++) {
s.push(e[i+j] ^ frame.MaskingKey[j%4]);
}
} else {
s = e.slice(i, i+frame.PayloadLength);
}
 
s = new Buffer(s);
 
if(frame.Opcode === 1) {
s = s.toString();
}
 
frame.PayloadData = s;
return frame;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function decodeDataFrame(e) {
var i = 0,
j,s,
frame = {
FIN: e[i] >> 7,
Opcode: e[i++] & 15,
Mask: e[i] >> 7,
PayloadLength: e[i++] & 0x7F
};
 
if(frame.PayloadLength === 126) {
frame.PayloadLength = (e[i++] << 8) + e[i++];
}
 
if(frame.PayloadLength === 127) {
i += 4;
frame.PayloadLength = (e[i++] << 24) + (e[i++] << 16) + (e[i++] << 8) + e[i++];
}
 
if(frame.Mask) {
frame.MaskingKey = [e[i++], e[i++], e[i++], e[i++]];
 
for(j = 0, s = []; j < frame.PayloadLength; j++) {
s.push(e[i+j] ^ frame.MaskingKey[j%4]);
}
} else {
s = e.slice(i, i+frame.PayloadLength);
}
 
s = new Buffer(s);
 
if(frame.Opcode === 1) {
s = s.toString();
}
 
frame.PayloadData = s;
return frame;
}

下一场是变化数据帧的

然后是浮动数据帧的

下一场是生成数据帧的

JavaScript

JavaScript

JavaScript

function encodeDataFrame(e) { var s = [], o = new
Buffer(e.PayloadData), l = o.length; s.push((e.FIN << 7) +
e.Opcode); if(l < 126) { s.push(l); } else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0,
0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00)
>> 8, l&0xFF); } return Buffer.concat([new Buffer(s), o]); }

function encodeDataFrame(e) { var s = [], o = new
Buffer(e.PayloadData), l = o.length; s.push((e.FIN << 7) +
e.Opcode); if(l < 126) { s.push(l); } else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0,
0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00)
>> 8, l&0xFF); } return Buffer.concat([new Buffer(s), o]); }

function encodeDataFrame(e) { var s = [], o = new
Buffer(e.PayloadData), l = o.length; s.push((e.FIN << 7) +
e.Opcode); if(l < 126) { s.push(l); } else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0,
0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00)
>> 8, l&0xFF); } return Buffer.concat([new Buffer(s), o]); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function encodeDataFrame(e) {
var s = [],
o = new Buffer(e.PayloadData),
l = o.length;
 
s.push((e.FIN << 7) + e.Opcode);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), o]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function encodeDataFrame(e) {
var s = [],
o = new Buffer(e.PayloadData),
l = o.length;
 
s.push((e.FIN << 7) + e.Opcode);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), o]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function encodeDataFrame(e) {
var s = [],
o = new Buffer(e.PayloadData),
l = o.length;
 
s.push((e.FIN << 7) + e.Opcode);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), o]);
}

都是依据帧结构示意图上的去处理,在那里不细讲,作品主要在下部分,如若对那块感兴趣的话能够移动web技术探究所~

都是根据帧结构示意图上的去处理,在那边不细讲,小说紧要在下有些,假若对那块感兴趣的话可以活动web技术商讨所~

都是循序渐进帧结构示意图上的去处理,在那边不细讲,小说主要在下一些,如果对那块感兴趣的话可以活动web技术探讨所~

 

 

 

三、websocket传输图片和websocket语音聊天室

三、websocket传输图片和websocket语音聊天室

三、websocket传输图片和websocket语音聊天室

正片环节到了,那篇小说最要紧的要么显得一下websocket的有的行使情况

正片环节到了,那篇文章最重点的或者显得一下websocket的一部分使用意况

正片环节到了,那篇小说最紧要的或者突显一下websocket的有些施用情况

1、传输图片

1、传输图片

1、传输图片

大家先探讨传输图片的步骤是何等,首先服务器收到到客户端请求,然后读取图片文件,将二进制数据转发给客户端,客户端如何处理?当然是行使FileReader对象了

俺们先思考传输图片的手续是如何,首先服务器收到到客户端请求,然后读取图片文件,将二进制数据转载给客户端,客户端怎样处理?当然是行使FileReader对象了

大家先思考传输图片的步调是哪些,首先服务器收到到客户端请求,然后读取图片文件,将二进制数据转发给客户端,客户端怎样处理?当然是运用FileReader对象了

先给客户端代码

先给客户端代码

先给客户端代码

JavaScript

JavaScript

JavaScript

var ws = new WebSocket(“ws://xxx.xxx.xxx.xxx:8888”); ws.onopen =
function(){ console.log(“握手成功”); }; ws.onmessage = function(e) { var
reader = new FileReader(); reader.onload = function(event) { var
contents = event.target.result; var a = new Image(); a.src = contents;
document.body.appendChild(a); } reader.readAsDataURL(e.data); };

var ws = new WebSocket(“ws://xxx.xxx.xxx.xxx:8888”); ws.onopen =
function(){ console.log(“握手成功”); }; ws.onmessage = function(e) { var
reader = new File里德(Reade)r(); reader.onload = function(event) { var
contents = event.target.result; var a = new Image(); a.src = contents;
document.body.appendChild(a); } reader.readAsDataURL(e.data); };

var ws = new WebSocket(“ws://xxx.xxx.xxx.xxx:8888”); ws.onopen =
function(){ console.log(“握手成功”); }; ws.onmessage = function(e) { var
reader = new File里德(Reade)r(); reader.onload = function(event) { var
contents = event.target.result; var a = new Image(); a.src = contents;
document.body.appendChild(a); } reader.readAsDataURL(e.data); };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ws = new WebSocket("ws://xxx.xxx.xxx.xxx:8888");
 
ws.onopen = function(){
    console.log("握手成功");
};
 
ws.onmessage = function(e) {
    var reader = new FileReader();
    reader.onload = function(event) {
        var contents = event.target.result;
        var a = new Image();
        a.src = contents;
        document.body.appendChild(a);
    }
    reader.readAsDataURL(e.data);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ws = new WebSocket("ws://xxx.xxx.xxx.xxx:8888");
 
ws.onopen = function(){
    console.log("握手成功");
};
 
ws.onmessage = function(e) {
    var reader = new FileReader();
    reader.onload = function(event) {
        var contents = event.target.result;
        var a = new Image();
        a.src = contents;
        document.body.appendChild(a);
    }
    reader.readAsDataURL(e.data);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ws = new WebSocket("ws://xxx.xxx.xxx.xxx:8888");
 
ws.onopen = function(){
    console.log("握手成功");
};
 
ws.onmessage = function(e) {
    var reader = new FileReader();
    reader.onload = function(event) {
        var contents = event.target.result;
        var a = new Image();
        a.src = contents;
        document.body.appendChild(a);
    }
    reader.readAsDataURL(e.data);
};

接收到消息,然后readAsDataURL,直接将图纸base64添加到页面中

收纳到音讯,然后readAsDataURL,直接将图纸base64添加到页面中

吸收到新闻,然后readAsDataURL,直接将图片base64添加到页面中

转到服务器端代码

转到服务器端代码

转到服务器端代码

JavaScript

JavaScript

JavaScript

fs.readdir(“skyland”, function(err, files) { if(err) { throw err; }
for(var i = 0; i < files.length; i++) { fs.readFile(‘skyland/’ +
files[i], function(err, data) { if(err) { throw err; }
o.write(encodeImgFrame(data)); }); } }); function encodeImgFrame(buf) {
var s = [], l = buf.length, ret = []; s.push((1 << 7) + 2);
if(l < 126) { s.push(l); } else if(l < 0x10000) { s.push(126,
(l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0, 0, 0,
(l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00)
>> 8, l&0xFF); } return Buffer.concat([new Buffer(s), buf]); }

fs.readdir(“skyland”, function(err, files) { if(err) { throw err; }
for(var i = 0; i < files.length; i++) { fs.readFile(‘skyland/’ +
files[i], function(err, data) { if(err) { throw err; }
o.write(encodeImgFrame(data)); }); } }); function encodeImgFrame(buf) {
var s = [], l = buf.length, ret = []; s.push((1 << 7) + 2);
if(l < 126) { s.push(l); } else if(l < 0x10000) { s.push(126,
(l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0, 0, 0,
(l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00)
>> 8, l&0xFF); } return Buffer.concat([new Buffer(s), buf]); }

fs.readdir(“skyland”, function(err, files) { if(err) { throw err; }
for(var i = 0; i < files.length; i++) { fs.readFile(‘skyland/’ +
files[i], function(err, data) { if(err) { throw err; }
o.write(encodeImgFrame(data)); }); } }); function encodeImgFrame(buf) {
var s = [], l = buf.length, ret = []; s.push((1 << 7) + 2);
if(l < 126) { s.push(l); } else if(l < 0x10000) { s.push(126,
(l&0xFF00) >> 8, l&0xFF); } else { s.push(127, 0, 0, 0, 0,
(l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00)
>> 8, l&0xFF); } return Buffer.concat([new Buffer(s), buf]); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fs.readdir("skyland", function(err, files) {
if(err) {
throw err;
}
for(var i = 0; i < files.length; i++) {
fs.readFile(‘skyland/’ + files[i], function(err, data) {
if(err) {
throw err;
}
 
o.write(encodeImgFrame(data));
});
}
});
 
function encodeImgFrame(buf) {
var s = [],
l = buf.length,
ret = [];
 
s.push((1 << 7) + 2);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), buf]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fs.readdir("skyland", function(err, files) {
if(err) {
throw err;
}
for(var i = 0; i < files.length; i++) {
fs.readFile(‘skyland/’ + files[i], function(err, data) {
if(err) {
throw err;
}
 
o.write(encodeImgFrame(data));
});
}
});
 
function encodeImgFrame(buf) {
var s = [],
l = buf.length,
ret = [];
 
s.push((1 << 7) + 2);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), buf]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fs.readdir("skyland", function(err, files) {
if(err) {
throw err;
}
for(var i = 0; i < files.length; i++) {
fs.readFile(‘skyland/’ + files[i], function(err, data) {
if(err) {
throw err;
}
 
o.write(encodeImgFrame(data));
});
}
});
 
function encodeImgFrame(buf) {
var s = [],
l = buf.length,
ret = [];
 
s.push((1 << 7) + 2);
 
if(l < 126) {
s.push(l);
} else if(l < 0x10000) {
s.push(126, (l&0xFF00) >> 8, l&0xFF);
} else {
s.push(127, 0, 0, 0, 0, (l&0xFF000000) >> 24, (l&0xFF0000) >> 16, (l&0xFF00) >> 8, l&0xFF);
}
 
return Buffer.concat([new Buffer(s), buf]);
}

注意s.push((1 << 7) +
2)
这一句,那里分外直接把opcode写死了为2,对于Binary
Frame,那样客户端接收到多少是不会尝试举行toString的,否则会报错~

注意s.push((1 << 7) +
2)
这一句,那里很是间接把opcode写死了为2,对于Binary
Frame,那样客户端接收到多少是不会尝试进行toString的,否则会报错~

注意s.push((1 << 7) +
2)
这一句,那里极度间接把opcode写死了为2,对于Binary
Frame,这样客户端接收到多少是不会尝试举办toString的,否则会报错~

代码很粗略,在此间向大家大快朵颐一下websocket传输图片的速度怎样

代码很粗略,在此地向大家大饱眼福一下websocket传输图片的快慢怎么着

代码很简单,在这里向咱们享受一下websocket传输图片的快慢如何

测试很多张图纸,总共8.24M

测试很多张图纸,总共8.24M

测试很多张图纸,总共8.24M

一般性静态资源服务器要求20s左右(服务器较远)

一般而言静态资源服务器必要20s左右(服务器较远)

普通静态资源服务器须要20s左右(服务器较远)

cdn需要2.8s左右

cdn需要2.8s左右

cdn需要2.8s左右

那我们的websocket形式吧??!

这我们的websocket方式吗??!

那大家的websocket情势吧??!

答案是同样须求20s左右,是否很失望……速度就是慢在传输上,并不是服务器读取图片,本机上亦然的图形资源,1s左右得以成功……那样看来数据流也不可能冲破距离的限制进步传输速度

答案是如出一辙需求20s左右,是还是不是很失望……速度就是慢在传输上,并不是服务器读取图片,本机上同一的图样资源,1s左右得以形成……这样看来数据流也无从冲破距离的界定升高传输速度

答案是平等须要20s左右,是还是不是很失望……速度就是慢在传输上,并不是服务器读取图片,本机上一样的图片资源,1s左右足以形成……那样看来数据流也无能为力冲破距离的范围进步传输速度

上边大家来探视websocket的另一个用法~

上面我们来探望websocket的另一个用法~

上边大家来看看websocket的另一个用法~

 

 

 

用websocket搭建语音聊天室

用websocket搭建语音聊天室

用websocket搭建语音聊天室

先来整治一下口音聊天室的法力

先来打点一下口音聊天室的作用

先来整治一下口音聊天室的功能

用户进入频道随后从Mike风输入音频,然后发送给后台转载给频道里面的其余人,其别人接收到新闻进行播报

用户进入频道随后从Mike风输入音频,然后发送给后台转发给频道里面的其余人,其余人接收到消息举行广播

用户进入频道随后从麦克(Mike)风输入音频,然后发送给后台转载给频道里面的其余人,别的人接收到消息举办播报

看起来困难在八个地点,第二个是节奏的输入,第二是接到到数量流进行播放

看起来困难在五个地方,第二个是节奏的输入,第二是吸纳到多少流举办播报

看起来困难在三个地方,第三个是节奏的输入,第二是收纳到数量流进行广播

先说音频的输入,那里运用了HTML5的getUserMedia方法,不过注意了,以此办法上线是有大坑的,最终说,先贴代码

先说音频的输入,那里运用了HTML5的getUserMedia方法,不过注意了,其一办法上线是有大坑的,最终说,先贴代码

先说音频的输入,那里运用了HTML5的getUserMedia方法,可是注意了,那些方法上线是有大坑的,最终说,先贴代码

JavaScript

JavaScript

JavaScript

if (navigator.getUserMedia) { navigator.getUserMedia( { audio: true },
function (stream) { var rec = new SRecorder(stream); recorder = rec; })
}

if (navigator.getUserMedia) { navigator.getUserMedia( { audio: true },
function (stream) { var rec = new SRecorder(stream); recorder = rec; })
}

if (navigator.getUserMedia) { navigator.getUserMedia( { audio: true },
function (stream) { var rec = new SRecorder(stream); recorder = rec; })
}

1
2
3
4
5
6
7
8
if (navigator.getUserMedia) {
    navigator.getUserMedia(
        { audio: true },
        function (stream) {
            var rec = new SRecorder(stream);
            recorder = rec;
        })
}
1
2
3
4
5
6
7
8
if (navigator.getUserMedia) {
    navigator.getUserMedia(
        { audio: true },
        function (stream) {
            var rec = new SRecorder(stream);
            recorder = rec;
        })
}
1
2
3
4
5
6
7
8
if (navigator.getUserMedia) {
    navigator.getUserMedia(
        { audio: true },
        function (stream) {
            var rec = new SRecorder(stream);
            recorder = rec;
        })
}

率先个参数是{audio:
true},只启用音频,然后创造了一个SRecorder对象,后续的操作基本上都在那个目的上实行。此时如果代码运行在当地的话浏览器应该提示您是还是不是启用麦克(Mike)风输入,确定今后就启动了

首先个参数是{audio:
true},只启用音频,然后成立了一个SRecorder对象,后续的操作基本上都在这一个目标上进行。此时只要代码运行在地面的话浏览器应该提示您是还是不是启用麦克(Mike)风输入,确定今后就开动了

首个参数是{audio:
true},只启用音频,然后创立了一个SRecorder对象,后续的操作基本上都在这几个目的上进行。此时一旦代码运行在当地的话浏览器应该提示您是还是不是启用迈克(Mike)风输入,确定之后就启动了

接下去大家看下SRecorder构造函数是啥,给出首要的一部分

接下去我们看下SRecorder构造函数是什么,给出首要的部分

接下去大家看下SRecorder构造函数是甚,给出紧要的局地

JavaScript

JavaScript

JavaScript

var SRecorder = function(stream) { …… var context = new AudioContext();
var audioInput = context.createMediaStreamSource(stream); var recorder =
context.createScriptProcessor(4096, 1, 1); …… }

var SRecorder = function(stream) { …… var context = new AudioContext();
var audioInput = context.createMediaStreamSource(stream); var recorder =
context.createScriptProcessor(4096, 1, 1); …… }

var SRecorder = function(stream) { …… var context = new AudioContext();
var audioInput = context.createMediaStreamSource(stream); var recorder =
context.createScriptProcessor(4096, 1, 1); …… }

1
2
3
4
5
6
7
var SRecorder = function(stream) {
    ……
   var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
    ……
}
1
2
3
4
5
6
7
var SRecorder = function(stream) {
    ……
   var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
    ……
}
1
2
3
4
5
6
7
var SRecorder = function(stream) {
    ……
   var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
    ……
}

奥迪(Audi)oContext是一个节奏上下文对象,有做过声音过滤处理的同室应该驾驭“一段音频到达扬声器举办广播此前,半路对其展开拦截,于是大家就得到了拍子数据了,这一个拦截工作是由window.奥迪(Audi)oContext来做的,大家具备对旋律的操作都根据那些目的”,我们得以因此奥迪(Audi)oContext成立不一致的奥迪(Audi)oNode节点,然后添加滤镜播放特其余声音

奥迪(Audi)oContext是一个节奏上下文对象,有做过声音过滤处理的同室应该精晓“一段音频到达扬声器进行播报此前,半路对其进展阻挠,于是咱们就得到了节奏数据了,那些拦截工作是由window.奥迪(Audi)oContext来做的,大家有着对旋律的操作都按照这些目的”,我们得以经过奥迪oContext成立区其余奥迪oNode节点,然后添加滤镜播放特其余动静

奥迪oContext是一个旋律上下文对象,有做过声音过滤处理的同校应该清楚“一段音频到达扬声器举办播放从前,半路对其开展拦阻,于是大家就得到了旋律数据了,这几个拦截工作是由window.奥迪(Audi)oContext来做的,大家拥有对旋律的操作都基于那一个目的”,我们可以透过奥迪(Audi)oContext创立分裂的奥迪oNode节点,然后添加滤镜播放特其他响声

录音原理一样,大家也须求走奥迪oContext,但是多了一步对Mike风音频输入的接受上,而不是像以往处理音频一下用ajax请求音频的ArrayBuffer对象再decode,迈克(Mike)风的接受须要用到createMediaStreamSource方法,注意那几个参数就是getUserMedia方法第一个参数的参数

录音原理一样,大家也亟需走奥迪(Audi)oContext,可是多了一步对迈克风音频输入的选择上,而不是像过去处理音频一下用ajax请求音频的ArrayBuffer对象再decode,麦克(Mike)风的收受必要用到createMediaStreamSource方法,注意这么些参数就是getUserMedia方法第三个参数的参数

录音原理一样,我们也亟需走奥迪(Audi)oContext,可是多了一步对麦克(Mike)风音频输入的接纳上,而不是像以往处理音频一下用ajax请求音频的ArrayBuffer对象再decode,迈克(Mike)风的收受须求用到createMediaStreamSource方法,注意那几个参数就是getUserMedia方法首个参数的参数

加以createScriptProcessor方法,它官方的表达是:

再则createScriptProcessor方法,它官方的解释是:

况且createScriptProcessor方法,它官方的分解是:

Creates a ScriptProcessorNode, which can be used for direct audio
processing via JavaScript.

Creates a ScriptProcessorNode, which can be used for direct audio
processing via JavaScript.

Creates a ScriptProcessorNode, which can be used for direct audio
processing via JavaScript.

——————

——————

——————

包罗下就是这么些点子是采取JavaScript去处理音频采集操作

包蕴下就是那一个艺术是选择JavaScript去处理音频采集操作

包括下就是这几个法子是利用JavaScript去处理音频采集操作

好不不难到点子采集了!胜利就在眼前!

终于到点子采集了!胜利就在前边!

毕竟到点子采集了!胜利就在前方!

接下去让大家把话筒的输入和旋律采集相连起来

接下去让大家把Mike风的输入和拍子采集相连起来

接下去让我们把话筒的输入和旋律采集相连起来

JavaScript

JavaScript

JavaScript

audioInput.connect(recorder); recorder.connect(context.destination);

audioInput.connect(recorder); recorder.connect(context.destination);

audioInput.connect(recorder); recorder.connect(context.destination);

1
2
audioInput.connect(recorder);
recorder.connect(context.destination);
1
2
audioInput.connect(recorder);
recorder.connect(context.destination);
1
2
audioInput.connect(recorder);
recorder.connect(context.destination);

context.destination官方表明如下

context.destination官方表明如下

context.destination官方解释如下

The destination property of
the AudioContext interface
returns
an AudioDestinationNoderepresenting
the final destination of all audio in the context.

The destination property of
the AudioContext interface
returns
an AudioDestinationNoderepresenting
the final destination of all audio in the context.

The destination property of
the AudioContext interface
returns
an AudioDestinationNoderepresenting
the final destination of all audio in the context.

——————

——————

——————

context.destination再次回到代表在条件中的音频的末梢目标地。

context.destination再次来到代表在条件中的音频的尾声目标地。

context.destination再次回到代表在条件中的音频的终极目标地。

好,到了此时,大家还亟需一个监听音频采集的事件

好,到了那儿,大家还亟需一个监听音频采集的事件

好,到了此时,我们还亟需一个监听音频采集的事件

JavaScript

JavaScript

JavaScript

recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0)); }

recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0)); }

recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0)); }

1
2
3
recorder.onaudioprocess = function (e) {
    audioData.input(e.inputBuffer.getChannelData(0));
}
1
2
3
recorder.onaudioprocess = function (e) {
    audioData.input(e.inputBuffer.getChannelData(0));
}
1
2
3
recorder.onaudioprocess = function (e) {
    audioData.input(e.inputBuffer.getChannelData(0));
}

audioData是一个目的,那个是在网上找的,我就加了一个clear方法因为前面会用到,首要有足够encodeWAV方法很赞,外人进行了累累的音频压缩和优化,这么些最后会伴随完整的代码一起贴出来

audioData是一个目的,这些是在网上找的,我就加了一个clear方法因为前边会用到,主要有充裕encodeWAV方法很赞,别人进行了往往的节拍压缩和优化,那几个最后会伴随完整的代码一起贴出来

audioData是一个目的,那个是在网上找的,我就加了一个clear方法因为背后会用到,首要有充足encodeWAV方法很赞,别人举行了频仍的节奏压缩和优化,这一个最后会伴随完整的代码一起贴出来

那会儿总体用户进入频道随后从迈克(Mike)风输入音频环节就曾经达成啦,上边就该是向劳动器端发送音频流,稍微有点蛋疼的来了,刚才大家说了,websocket通过opcode分歧可以象征回去的数量是文件仍然二进制数据,而我辈onaudioprocess中input进去的是数组,最后播放声音必要的是Blob,{type:
‘audio/wav’}的目的,那样我们就亟要求在殡葬以前将数组转换成WAV的Blob,此时就用到了地方说的encodeWAV方法

那会儿所有用户进入频道随后从Mike风输入音频环节就已经已毕啦,下边就该是向服务器端发送音频流,稍微有点蛋疼的来了,刚才我们说了,websocket通过opcode分歧可以表示回去的数额是文件依旧二进制数据,而大家onaudioprocess中input进去的是数组,最后播放音响需求的是Blob,{type:
‘audio/wav’}的对象,那样我们就非得要在发送从前将数组转换成WAV的Blob,此时就用到了下边说的encodeWAV方法

那会儿全部用户进入频道随后从迈克(Mike)风输入音频环节就已经完毕啦,上边就该是向服务器端发送音频流,稍微有点蛋疼的来了,刚才大家说了,websocket通过opcode差距可以表示回去的数目是文件依旧二进制数据,而大家onaudioprocess中input进去的是数组,最后播放音响需求的是Blob,{type:
‘audio/wav’}的对象,这样大家就必须求在发送在此之前将数组转换成WAV的Blob,此时就用到了下面说的encodeWAV方法

服务器就像很简单,只要转载就行了

服务器如同很粗略,只要转载就行了

服务器就如很简短,只要转发就行了

本地测试确实可以,然而天坑来了!将顺序跑在服务器上时候调用getUserMedia方法提示我无法不在一个有惊无险的环境,也就是亟需https,那象征ws也不可以不换成wss……之所以服务器代码就从未有过使用我们自己包裹的拉手、解析和编码了,代码如下

地面测试确实能够,只是天坑来了!将先后跑在服务器上时候调用getUserMedia方法提醒我必须在一个平安的条件,也就是必要https,那表示ws也务必换成wss……就此服务器代码就一直不行使大家团结包装的握手、解析和编码了,代码如下

当地测试确实可以,只是天坑来了!将次第跑在服务器上时候调用getUserMedia方法提醒我必须在一个白城的环境,也就是内需https,那意味着ws也亟须换成wss……由此服务器代码就没有利用我们团结包装的拉手、解析和编码了,代码如下

JavaScript

JavaScript

JavaScript

var https = require(‘https’); var fs = require(‘fs’); var ws =
require(‘ws’); var userMap = Object.create(null); var options = { key:
fs.readFileSync(‘./privatekey.pem’), cert:
fs.readFileSync(‘./certificate.pem’) }; var server =
https.createServer(options, function(req, res) { res.writeHead({
‘Content-Type’ : ‘text/html’ }); fs.readFile(‘./testaudio.html’,
function(err, data) { if(err) { return ; } res.end(data); }); }); var
wss = new ws.Server({server: server}); wss.on(‘connection’, function(o)
{ o.on(‘message’, function(message) { if(message.indexOf(‘user’) === 0)
{ var user = message.split(‘:’)[1]; userMap[user] = o; } else {
for(var u in userMap) { userMap[u].send(message); } } }); });
server.listen(8888);

var https = require(‘https’); var fs = require(‘fs’); var ws =
require(‘ws’); var userMap = Object.create(null); var options = { key:
fs.readFileSync(‘./privatekey.pem’), cert:
fs.readFileSync(‘./certificate.pem’) }; var server =
https.createServer(options, function(req, res) { res.writeHead({
‘Content-Type’ : ‘text/html’ }); fs.readFile(‘./testaudio.html’,
function(err, data) { if(err) { return ; } res.end(data); }); }); var
wss = new ws.Server({server: server}); wss.on(‘connection’, function(o)
{ o.on(‘message’, function(message) { if(message.indexOf(‘user’) === 0)
{ var user = message.split(‘:’)[1]; userMap[user] = o; } else {
for(var u in userMap) { userMap[u].send(message); } } }); });
server.listen(8888);

var https = require(‘https’); var fs = require(‘fs’); var ws =
require(‘ws’); var userMap = Object.create(null); var options = { key:
fs.readFileSync(‘./privatekey.pem’), cert:
fs.readFileSync(‘./certificate.pem’) }; var server =
https.createServer(options, function(req, res) { res.writeHead({
‘Content-Type’ : ‘text/html’ }); fs.readFile(‘./testaudio.html’,
function(err, data) { if(err) { return ; } res.end(data); }); }); var
wss = new ws.Server({server: server}); wss.on(‘connection’, function(o)
{ o.on(‘message’, function(message) { if(message.indexOf(‘user’) === 0)
{ var user = message.split(‘:’)[1]; userMap[user] = o; } else {
for(var u in userMap) { userMap[u].send(message); } } }); });
server.listen(8888);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var https = require(‘https’);
var fs = require(‘fs’);
var ws = require(‘ws’);
var userMap = Object.create(null);
var options = {
    key: fs.readFileSync(‘./privatekey.pem’),
    cert: fs.readFileSync(‘./certificate.pem’)
};
var server = https.createServer(options, function(req, res) {
    res.writeHead({
        ‘Content-Type’ : ‘text/html’
    });
 
    fs.readFile(‘./testaudio.html’, function(err, data) {
        if(err) {
            return ;
        }
 
        res.end(data);
    });
});
 
var wss = new ws.Server({server: server});
 
wss.on(‘connection’, function(o) {
    o.on(‘message’, function(message) {
if(message.indexOf(‘user’) === 0) {
    var user = message.split(‘:’)[1];
    userMap[user] = o;
} else {
    for(var u in userMap) {
userMap[u].send(message);
    }
}
    });
});
 
server.listen(8888);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var https = require(‘https’);
var fs = require(‘fs’);
var ws = require(‘ws’);
var userMap = Object.create(null);
var options = {
    key: fs.readFileSync(‘./privatekey.pem’),
    cert: fs.readFileSync(‘./certificate.pem’)
};
var server = https.createServer(options, function(req, res) {
    res.writeHead({
        ‘Content-Type’ : ‘text/html’
    });
 
    fs.readFile(‘./testaudio.html’, function(err, data) {
        if(err) {
            return ;
        }
 
        res.end(data);
    });
});
 
var wss = new ws.Server({server: server});
 
wss.on(‘connection’, function(o) {
    o.on(‘message’, function(message) {
if(message.indexOf(‘user’) === 0) {
    var user = message.split(‘:’)[1];
    userMap[user] = o;
} else {
    for(var u in userMap) {
userMap[u].send(message);
    }
}
    });
});
 
server.listen(8888);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var https = require(‘https’);
var fs = require(‘fs’);
var ws = require(‘ws’);
var userMap = Object.create(null);
var options = {
    key: fs.readFileSync(‘./privatekey.pem’),
    cert: fs.readFileSync(‘./certificate.pem’)
};
var server = https.createServer(options, function(req, res) {
    res.writeHead({
        ‘Content-Type’ : ‘text/html’
    });
 
    fs.readFile(‘./testaudio.html’, function(err, data) {
        if(err) {
            return ;
        }
 
        res.end(data);
    });
});
 
var wss = new ws.Server({server: server});
 
wss.on(‘connection’, function(o) {
    o.on(‘message’, function(message) {
if(message.indexOf(‘user’) === 0) {
    var user = message.split(‘:’)[1];
    userMap[user] = o;
} else {
    for(var u in userMap) {
userMap[u].send(message);
    }
}
    });
});
 
server.listen(8888);

代码如故很粗略的,使用https模块,然后用了开首说的ws模块,userMap是仿照的频道,只兑现转载的中坚作用

代码仍然很简短的,使用https模块,然后用了开班说的ws模块,userMap是人云亦云的频段,只兑现转发的主导功效

代码如故很粗略的,使用https模块,然后用了启幕说的ws模块,userMap是模仿的频段,只兑现转发的着力效能

使用ws模块是因为它非常https完结wss实在是太便宜了,和逻辑代码0争论

选用ws模块是因为它万分https完毕wss实在是太有利了,和逻辑代码0争论

接纳ws模块是因为它突出https完毕wss实在是太方便了,和逻辑代码0争执

https的搭建在这边就不提了,紧如果亟需私钥、CSR证书签名和证书文件,感兴趣的校友可以驾驭下(然而不精通的话在现网环境也用持续getUserMedia……)

https的搭建在此处就不提了,主如若须要私钥、CSR证书签名和申明文件,感兴趣的同桌可以通晓下(可是不明白的话在现网环境也用持续getUserMedia……)

https的搭建在此地就不提了,重假诺急需私钥、CSR证书签名和证书文件,感兴趣的同校可以通晓下(但是不明白的话在现网环境也用持续getUserMedia……)

上面是完好的前端代码

上边是总体的前端代码

上边是一体化的前端代码

JavaScript

JavaScript

JavaScript

var a = document.getElementById(‘a’); var b =
document.getElementById(‘b’); var c = document.getElementById(‘c’);
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia; var gRecorder = null; var audio =
document.querySelector(‘audio’); var door = false; var ws = null;
b.onclick = function() { if(a.value === ”) { alert(‘请输入用户名’);
return false; } if(!navigator.getUserMedia) {
alert(‘抱歉您的装置无爱沙尼亚语音聊天’); return false; }
SRecorder.get(function (rec) { gRecorder = rec; }); ws = new
WebSocket(“wss://x.x.x.x:8888”); ws.onopen = function() {
console.log(‘握手成功’); ws.send(‘user:’ + a.value); }; ws.onmessage =
function(e) { receive(e.data); }; document.onkeydown = function(e) {
if(e.keyCode === 65) { if(!door) { gRecorder.start(); door = true; } }
}; document.onkeyup = function(e) { if(e.keyCode === 65) { if(door) {
ws.send(gRecorder.getBlob()); gRecorder.clear(); gRecorder.stop(); door
= false; } } } } c.onclick = function() { if(ws) { ws.close(); } } var
SRecorder = function(stream) { config = {}; config.sampleBits =
config.smapleBits || 8; config.sampleRate = config.sampleRate || (44100
/ 6); var context = new 奥迪oContext(); var audioInput =
context.createMediaStreamSource(stream); var recorder =
context.createScriptProcessor(4096, 1, 1); var audioData = { size: 0
//录音文件长度 , buffer: [] //录音缓存 , input山姆(Sam)pleRate:
context.sampleRate //输入采样率 , input山姆pleBits: 16 //输入采样数位 8,
16 , output山姆pleRate: config.sampleRate //输出采样率 , outut山姆pleBits:
config.sampleBits //输出采样数位 8, 16 , clear: function() { this.buffer
= []; this.size = 0; } , input: function (data) { this.buffer.push(new
Float32Array(data)); this.size += data.length; } , compress: function ()
{ //合并压缩 //合并 var data = new Float32Array(this.size); var offset =
0; for (var i = 0; i < this.buffer.length; i++) {
data.set(this.buffer[i], offset); offset += this.buffer[i].length; }
//压缩 var compression = parseInt(this.inputSampleRate /
this.outputSampleRate); var length = data.length / compression; var
result = new Float32Array(length); var index = 0, j = 0; while (index
< length) { result[index] = data[j]; j += compression; index++; }
return result; } , encodeWAV: function () { var sampleRate =
Math.min(this.inputSampleRate, this.outputSampleRate); var sampleBits =
Math.min(this.inputSampleBits, this.oututSampleBits); var bytes =
this.compress(); var dataLength = bytes.length * (sampleBits / 8); var
buffer = new ArrayBuffer(44 + dataLength); var data = new
DataView(buffer); var channelCount = 1;//单声道 var offset = 0; var
writeString = function (str) { for (var i = 0; i < str.length; i++) {
data.setUint8(offset + i, str.charCodeAt(i)); } }; // 资源交流文件标识符
writeString(‘RIFF’); offset += 4; //
下个地点初步到文件尾总字节数,即文件大小-8 data.setUint32(offset, 36 +
dataLength, true); offset += 4; // WAV文件注明 writeString(‘WAVE’);
offset += 4; // 波形格式标志 writeString(‘fmt ‘); offset += 4; //
过滤字节,一般为 0x10 = 16 data.setUint32(offset, 16, true); offset += 4;
// 格式系列 (PCM情势采样数据) data.setUint16(offset, 1, true); offset +=
2; // 通道数 data.setUint16(offset, channelCount, true); offset += 2; //
采样率,每秒样本数,表示每个通道的广播速度 data.setUint32(offset,
sampleRate, true); offset += 4; // 波形数据传输率 (每秒平均字节数)
单声道×每秒数据位数×每样本数据位/8 data.setUint32(offset, channelCount
* sampleRate * (sampleBits / 8), true); offset += 4; // 快数据调整数
采样三次占用字节数 单声道×每样本的数据位数/8 data.setUint16(offset,
channelCount * (sampleBits / 8), true); offset += 2; // 每样本数量位数
data.setUint16(offset, sampleBits, true); offset += 2; // 数据标识符
writeString(‘data’); offset += 4; // 采样数据总数,即数据总大小-44
data.setUint32(offset, dataLength, true); offset += 4; // 写入采样数据
if (sampleBits === 8) { for (var i = 0; i < bytes.length; i++,
offset++) { var s = Math.max(-1, Math.min(1, bytes[i])); var val = s
< 0 ? s * 0x8000 : s * 0x7FFF; val = parseInt(255 / (65535 / (val +
32768))); data.setInt8(offset, val, true); } } else { for (var i = 0; i
< bytes.length; i++, offset += 2) { var s = Math.max(-1, Math.min(1,
bytes[i])); data.setInt16(offset, s < 0 ? s * 0x8000 : s *
0x7FFF, true); } } return new Blob([data], { type: ‘audio/wav’ }); }
}; this.start = function () { audioInput.connect(recorder);
recorder.connect(context.destination); } this.stop = function () {
recorder.disconnect(); } this.getBlob = function () { return
audioData.encodeWAV(); } this.clear = function() { audioData.clear(); }
recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0)); } }; SRecorder.get =
function (callback) { if (callback) { if (navigator.getUserMedia) {
navigator.getUserMedia( { audio: true }, function (stream) { var rec =
new SRecorder(stream); callback(rec); }) } } } function receive(e) {
audio.src = window.URL.createObjectURL(e); }

var a = document.getElementById(‘a’); var b =
document.getElementById(‘b’); var c = document.getElementById(‘c’);
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia; var gRecorder = null; var audio =
document.querySelector(‘audio’); var door = false; var ws = null;
b.onclick = function() { if(a.value === ”) { alert(‘请输入用户名’);
return false; } if(!navigator.getUserMedia) {
alert(‘抱歉您的装置无乌克兰语音聊天’); return false; }
SRecorder.get(function (rec) { gRecorder = rec; }); ws = new
WebSocket(“wss://x.x.x.x:8888”); ws.onopen = function() {
console.log(‘握手成功’); ws.send(‘user:’ + a.value); }; ws.onmessage =
function(e) { receive(e.data); }; document.onkeydown = function(e) {
if(e.keyCode === 65) { if(!door) { gRecorder.start(); door = true; } }
}; document.onkeyup = function(e) { if(e.keyCode === 65) { if(door) {
ws.send(gRecorder.getBlob()); gRecorder.clear(); gRecorder.stop(); door
= false; } } } } c.onclick = function() { if(ws) { ws.close(); } } var
SRecorder = function(stream) { config = {}; config.sampleBits =
config.smapleBits || 8; config.sampleRate = config.sampleRate || (44100
/ 6); var context = new 奥迪(Audi)oContext(); var audioInput =
context.createMediaStreamSource(stream); var recorder =
context.createScriptProcessor(4096, 1, 1); var audioData = { size: 0
//录音文件长度 , buffer: [] //录音缓存 , input山姆(Sam)pleRate:
context.sampleRate //输入采样率 , input山姆(Sam)pleBits: 16 //输入采样数位 8,
16 , output萨姆pleRate: config.sampleRate //输出采样率 , outut山姆pleBits:
config.sampleBits //输出采样数位 8, 16 , clear: function() { this.buffer
= []; this.size = 0; } , input: function (data) { this.buffer.push(new
Float32Array(data)); this.size += data.length; } , compress: function ()
{ //合并压缩 //合并 var data = new Float32Array(this.size); var offset =
0; for (var i = 0; i < this.buffer.length; i++) {
data.set(this.buffer[i], offset); offset += this.buffer[i].length; }
//压缩 var compression = parseInt(this.inputSampleRate /
this.outputSampleRate); var length = data.length / compression; var
result = new Float32Array(length); var index = 0, j = 0; while (index
< length) { result[index] = data[j]; j += compression; index++; }
return result; } , encodeWAV: function () { var sampleRate =
Math.min(this.inputSampleRate, this.outputSampleRate); var sampleBits =
Math.min(this.inputSampleBits, this.oututSampleBits); var bytes =
this.compress(); var dataLength = bytes.length * (sampleBits / 8); var
buffer = new ArrayBuffer(44 + dataLength); var data = new
DataView(buffer); var channelCount = 1;//单声道 var offset = 0; var
writeString = function (str) { for (var i = 0; i < str.length; i++) {
data.setUint8(offset + i, str.charCodeAt(i)); } }; // 资源调换文件标识符
writeString(‘RIFF’); offset += 4; //
下个地点起始到文件尾总字节数,即文件大小-8 data.setUint32(offset, 36 +
dataLength, true); offset += 4; // WAV文件注脚 writeString(‘WAVE’);
offset += 4; // 波形格式标志 writeString(‘fmt ‘); offset += 4; //
过滤字节,一般为 0x10 = 16 data.setUint32(offset, 16, true); offset += 4;
// 格式系列 (PCM方式采样数据) data.setUint16(offset, 1, true); offset +=
2; // 通道数 data.setUint16(offset, channelCount, true); offset += 2; //
采样率,每秒样本数,表示每个通道的播音速度 data.setUint32(offset,
sampleRate, true); offset += 4; // 波形数据传输率 (每秒平均字节数)
单声道×每秒数据位数×每样本数据位/8 data.setUint32(offset, channelCount
* sampleRate * (sampleBits / 8), true); offset += 4; // 快数据调整数
采样三次占用字节数 单声道×每样本的数额位数/8 data.setUint16(offset,
channelCount * (sampleBits / 8), true); offset += 2; // 每样本数量位数
data.setUint16(offset, sampleBits, true); offset += 2; // 数据标识符
writeString(‘data’); offset += 4; // 采样数据总数,即数据总大小-44
data.setUint32(offset, dataLength, true); offset += 4; // 写入采样数据
if (sampleBits === 8) { for (var i = 0; i < bytes.length; i++,
offset++) { var s = Math.max(-1, Math.min(1, bytes[i])); var val = s
< 0 ? s * 0x8000 : s * 0x7FFF; val = parseInt(255 / (65535 / (val +
32768))); data.setInt8(offset, val, true); } } else { for (var i = 0; i
< bytes.length; i++, offset += 2) { var s = Math.max(-1, Math.min(1,
bytes[i])); data.setInt16(offset, s < 0 ? s * 0x8000 : s *
0x7FFF, true); } } return new Blob([data], { type: ‘audio/wav’ }); }
}; this.start = function () { audioInput.connect(recorder);
recorder.connect(context.destination); } this.stop = function () {
recorder.disconnect(); } this.getBlob = function () { return
audioData.encodeWAV(); } this.clear = function() { audioData.clear(); }
recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0)); } }; SRecorder.get =
function (callback) { if (callback) { if (navigator.getUserMedia) {
navigator.getUserMedia( { audio: true }, function (stream) { var rec =
new SRecorder(stream); callback(rec); }) } } } function receive(e) {
audio.src = window.URL.createObjectURL(e); }

var a = document.getElementById(‘a’); var b =
document.getElementById(‘b’); var c = document.getElementById(‘c’);
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia; var gRecorder = null; var audio =
document.querySelector(‘audio’); var door = false; var ws = null;
b.onclick = function() { if(a.value === ”) { alert(‘请输入用户名’);
return false; } if(!navigator.getUserMedia) {
alert(‘抱歉您的设施无日语音聊天’); return false; }
SRecorder.get(function (rec) { gRecorder = rec; }); ws = new
WebSocket(“wss://x.x.x.x:8888”); ws.onopen = function() {
console.log(‘握手成功’); ws.send(‘user:’ + a.value); }; ws.onmessage =
function(e) { receive(e.data); }; document.onkeydown = function(e) {
if(e.keyCode === 65) { if(!door) { gRecorder.start(); door = true; } }
}; document.onkeyup = function(e) { if(e.keyCode === 65) { if(door) {
ws.send(gRecorder.getBlob()); gRecorder.clear(); gRecorder.stop(); door
= false; } } } } c.onclick = function() { if(ws) { ws.close(); } } var
SRecorder = function(stream) { config = {}; config.sampleBits =
config.smapleBits || 8; config.sampleRate = config.sampleRate || (44100
/ 6); var context = new 奥迪(Audi)oContext(); var audioInput =
context.createMediaStreamSource(stream); var recorder =
context.createScriptProcessor(4096, 1, 1); var audioData = { size: 0
//录音文件长度 , buffer: [] //录音缓存 , input萨姆pleRate:
context.sampleRate //输入采样率 , input萨姆(Sam)pleBits: 16 //输入采样数位 8,
16 , output山姆(Sam)pleRate: config.sampleRate //输出采样率 , outut萨姆(Sam)pleBits:
config.sampleBits //输出采样数位 8, 16 , clear: function() { this.buffer
= []; this.size = 0; } , input: function (data) { this.buffer.push(new
Float32Array(data)); this.size += data.length; } , compress: function ()
{ //合并压缩 //合并 var data = new Float32Array(this.size); var offset =
0; for (var i = 0; i < this.buffer.length; i++) {
data.set(this.buffer[i], offset); offset += this.buffer[i].length; }
//压缩 var compression = parseInt(this.inputSampleRate /
this.outputSampleRate); var length = data.length / compression; var
result = new Float32Array(length); var index = 0, j = 0; while (index
< length) { result[index] = data[j]; j += compression; index++; }
return result; } , encodeWAV: function () { var sampleRate =
Math.min(this.inputSampleRate, this.outputSampleRate); var sampleBits =
Math.min(this.inputSampleBits, this.oututSampleBits); var bytes =
this.compress(); var dataLength = bytes.length * (sampleBits / 8); var
buffer = new ArrayBuffer(44 + dataLength); var data = new
DataView(buffer); var channelCount = 1;//单声道 var offset = 0; var
writeString = function (str) { for (var i = 0; i < str.length; i++) {
data.setUint8(offset + i, str.charCodeAt(i)); } }; // 资源互换文件标识符
writeString(‘RIFF’); offset += 4; //
下个地方起头到文件尾总字节数,即文件大小-8 data.setUint32(offset, 36 +
dataLength, true); offset += 4; // WAV文件表明 writeString(‘WAVE’);
offset += 4; // 波形格式标志 writeString(‘fmt ‘); offset += 4; //
过滤字节,一般为 0x10 = 16 data.setUint32(offset, 16, true); offset += 4;
// 格式种类 (PCM情势采样数据) data.setUint16(offset, 1, true); offset +=
2; // 通道数 data.setUint16(offset, channelCount, true); offset += 2; //
采样率,每秒样本数,表示每个通道的播报速度 data.setUint32(offset,
sampleRate, true); offset += 4; // 波形数据传输率 (每秒平均字节数)
单声道×每秒数据位数×每样本数据位/8 data.setUint32(offset, channelCount
* sampleRate * (sampleBits / 8), true); offset += 4; // 快数据调整数
采样一回占用字节数 单声道×每样本的数量位数/8 data.setUint16(offset,
channelCount * (sampleBits / 8), true); offset += 2; // 每样本数量位数
data.setUint16(offset, sampleBits, true); offset += 2; // 数据标识符
writeString(‘data’); offset += 4; // 采样数据总数,即数据总大小-44
data.setUint32(offset, dataLength, true); offset += 4; // 写入采样数据
if (sampleBits === 8) { for (var i = 0; i < bytes.length; i++,
offset++) { var s = Math.max(-1, Math.min(1, bytes[i])); var val = s
< 0 ? s * 0x8000 : s * 0x7FFF; val = parseInt(255 / (65535 / (val +
32768))); data.setInt8(offset, val, true); } } else { for (var i = 0; i
< bytes.length; i++, offset += 2) { var s = Math.max(-1, Math.min(1,
bytes[i])); data.setInt16(offset, s < 0 ? s * 0x8000 : s *
0x7FFF, true); } } return new Blob([data], { type: ‘audio/wav’ }); }
}; this.start = function () { audioInput.connect(recorder);
recorder.connect(context.destination); } this.stop = function () {
recorder.disconnect(); } this.getBlob = function () { return
audioData.encodeWAV(); } this.clear = function() { audioData.clear(); }
recorder.onaudioprocess = function (e) {
audioData.input(e.inputBuffer.getChannelData(0)); } }; SRecorder.get =
function (callback) { if (callback) { if (navigator.getUserMedia) {
navigator.getUserMedia( { audio: true }, function (stream) { var rec =
new SRecorder(stream); callback(rec); }) } } } function receive(e) {
audio.src = window.URL.createObjectURL(e); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
var a = document.getElementById(‘a’);
var b = document.getElementById(‘b’);
var c = document.getElementById(‘c’);
 
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
 
var gRecorder = null;
var audio = document.querySelector(‘audio’);
var door = false;
var ws = null;
 
b.onclick = function() {
    if(a.value === ”) {
        alert(‘请输入用户名’);
        return false;
    }
    if(!navigator.getUserMedia) {
        alert(‘抱歉您的设备无法语音聊天’);
        return false;
    }
 
    SRecorder.get(function (rec) {
        gRecorder = rec;
    });
 
    ws = new WebSocket("wss://x.x.x.x:8888");
 
    ws.onopen = function() {
        console.log(‘握手成功’);
        ws.send(‘user:’ + a.value);
    };
 
    ws.onmessage = function(e) {
        receive(e.data);
    };
 
    document.onkeydown = function(e) {
        if(e.keyCode === 65) {
            if(!door) {
                gRecorder.start();
                door = true;
            }
        }
    };
 
    document.onkeyup = function(e) {
        if(e.keyCode === 65) {
            if(door) {
                ws.send(gRecorder.getBlob());
                gRecorder.clear();
                gRecorder.stop();
                door = false;
            }
        }
    }
}
 
c.onclick = function() {
    if(ws) {
        ws.close();
    }
}
 
var SRecorder = function(stream) {
    config = {};
 
    config.sampleBits = config.smapleBits || 8;
    config.sampleRate = config.sampleRate || (44100 / 6);
 
    var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
 
    var audioData = {
        size: 0          //录音文件长度
        , buffer: []     //录音缓存
        , inputSampleRate: context.sampleRate    //输入采样率
        , inputSampleBits: 16       //输入采样数位 8, 16
        , outputSampleRate: config.sampleRate    //输出采样率
        , oututSampleBits: config.sampleBits       //输出采样数位 8, 16
        , clear: function() {
            this.buffer = [];
            this.size = 0;
        }
        , input: function (data) {
            this.buffer.push(new Float32Array(data));
            this.size += data.length;
        }
        , compress: function () { //合并压缩
            //合并
            var data = new Float32Array(this.size);
            var offset = 0;
            for (var i = 0; i < this.buffer.length; i++) {
                data.set(this.buffer[i], offset);
                offset += this.buffer[i].length;
            }
            //压缩
            var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
            var length = data.length / compression;
            var result = new Float32Array(length);
            var index = 0, j = 0;
            while (index < length) {
                result[index] = data[j];
                j += compression;
                index++;
            }
            return result;
        }
        , encodeWAV: function () {
            var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
            var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
            var bytes = this.compress();
            var dataLength = bytes.length * (sampleBits / 8);
            var buffer = new ArrayBuffer(44 + dataLength);
            var data = new DataView(buffer);
 
            var channelCount = 1;//单声道
            var offset = 0;
 
            var writeString = function (str) {
                for (var i = 0; i < str.length; i++) {
                    data.setUint8(offset + i, str.charCodeAt(i));
                }
            };
 
            // 资源交换文件标识符
            writeString(‘RIFF’); offset += 4;
            // 下个地址开始到文件尾总字节数,即文件大小-8
            data.setUint32(offset, 36 + dataLength, true); offset += 4;
            // WAV文件标志
            writeString(‘WAVE’); offset += 4;
            // 波形格式标志
            writeString(‘fmt ‘); offset += 4;
            // 过滤字节,一般为 0x10 = 16
            data.setUint32(offset, 16, true); offset += 4;
            // 格式类别 (PCM形式采样数据)
            data.setUint16(offset, 1, true); offset += 2;
            // 通道数
            data.setUint16(offset, channelCount, true); offset += 2;
            // 采样率,每秒样本数,表示每个通道的播放速度
            data.setUint32(offset, sampleRate, true); offset += 4;
            // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
            data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
            // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
            data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
            // 每样本数据位数
            data.setUint16(offset, sampleBits, true); offset += 2;
            // 数据标识符
            writeString(‘data’); offset += 4;
            // 采样数据总数,即数据总大小-44
            data.setUint32(offset, dataLength, true); offset += 4;
            // 写入采样数据
            if (sampleBits === 8) {
                for (var i = 0; i < bytes.length; i++, offset++) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
                    val = parseInt(255 / (65535 / (val + 32768)));
                    data.setInt8(offset, val, true);
                }
            } else {
                for (var i = 0; i < bytes.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
            }
 
            return new Blob([data], { type: ‘audio/wav’ });
        }
    };
 
    this.start = function () {
        audioInput.connect(recorder);
        recorder.connect(context.destination);
    }
 
    this.stop = function () {
        recorder.disconnect();
    }
 
    this.getBlob = function () {
        return audioData.encodeWAV();
    }
 
    this.clear = function() {
        audioData.clear();
    }
 
    recorder.onaudioprocess = function (e) {
        audioData.input(e.inputBuffer.getChannelData(0));
    }
};
 
SRecorder.get = function (callback) {
    if (callback) {
        if (navigator.getUserMedia) {
            navigator.getUserMedia(
                { audio: true },
                function (stream) {
                    var rec = new SRecorder(stream);
                    callback(rec);
                })
        }
    }
}
 
function receive(e) {
    audio.src = window.URL.createObjectURL(e);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
var a = document.getElementById(‘a’);
var b = document.getElementById(‘b’);
var c = document.getElementById(‘c’);
 
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
 
var gRecorder = null;
var audio = document.querySelector(‘audio’);
var door = false;
var ws = null;
 
b.onclick = function() {
    if(a.value === ”) {
        alert(‘请输入用户名’);
        return false;
    }
    if(!navigator.getUserMedia) {
        alert(‘抱歉您的设备无法语音聊天’);
        return false;
    }
 
    SRecorder.get(function (rec) {
        gRecorder = rec;
    });
 
    ws = new WebSocket("wss://x.x.x.x:8888");
 
    ws.onopen = function() {
        console.log(‘握手成功’);
        ws.send(‘user:’ + a.value);
    };
 
    ws.onmessage = function(e) {
        receive(e.data);
    };
 
    document.onkeydown = function(e) {
        if(e.keyCode === 65) {
            if(!door) {
                gRecorder.start();
                door = true;
            }
        }
    };
 
    document.onkeyup = function(e) {
        if(e.keyCode === 65) {
            if(door) {
                ws.send(gRecorder.getBlob());
                gRecorder.clear();
                gRecorder.stop();
                door = false;
            }
        }
    }
}
 
c.onclick = function() {
    if(ws) {
        ws.close();
    }
}
 
var SRecorder = function(stream) {
    config = {};
 
    config.sampleBits = config.smapleBits || 8;
    config.sampleRate = config.sampleRate || (44100 / 6);
 
    var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
 
    var audioData = {
        size: 0          //录音文件长度
        , buffer: []     //录音缓存
        , inputSampleRate: context.sampleRate    //输入采样率
        , inputSampleBits: 16       //输入采样数位 8, 16
        , outputSampleRate: config.sampleRate    //输出采样率
        , oututSampleBits: config.sampleBits       //输出采样数位 8, 16
        , clear: function() {
            this.buffer = [];
            this.size = 0;
        }
        , input: function (data) {
            this.buffer.push(new Float32Array(data));
            this.size += data.length;
        }
        , compress: function () { //合并压缩
            //合并
            var data = new Float32Array(this.size);
            var offset = 0;
            for (var i = 0; i < this.buffer.length; i++) {
                data.set(this.buffer[i], offset);
                offset += this.buffer[i].length;
            }
            //压缩
            var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
            var length = data.length / compression;
            var result = new Float32Array(length);
            var index = 0, j = 0;
            while (index < length) {
                result[index] = data[j];
                j += compression;
                index++;
            }
            return result;
        }
        , encodeWAV: function () {
            var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
            var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
            var bytes = this.compress();
            var dataLength = bytes.length * (sampleBits / 8);
            var buffer = new ArrayBuffer(44 + dataLength);
            var data = new DataView(buffer);
 
            var channelCount = 1;//单声道
            var offset = 0;
 
            var writeString = function (str) {
                for (var i = 0; i < str.length; i++) {
                    data.setUint8(offset + i, str.charCodeAt(i));
                }
            };
 
            // 资源交换文件标识符
            writeString(‘RIFF’); offset += 4;
            // 下个地址开始到文件尾总字节数,即文件大小-8
            data.setUint32(offset, 36 + dataLength, true); offset += 4;
            // WAV文件标志
            writeString(‘WAVE’); offset += 4;
            // 波形格式标志
            writeString(‘fmt ‘); offset += 4;
            // 过滤字节,一般为 0x10 = 16
            data.setUint32(offset, 16, true); offset += 4;
            // 格式类别 (PCM形式采样数据)
            data.setUint16(offset, 1, true); offset += 2;
            // 通道数
            data.setUint16(offset, channelCount, true); offset += 2;
            // 采样率,每秒样本数,表示每个通道的播放速度
            data.setUint32(offset, sampleRate, true); offset += 4;
            // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
            data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
            // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
            data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
            // 每样本数据位数
            data.setUint16(offset, sampleBits, true); offset += 2;
            // 数据标识符
            writeString(‘data’); offset += 4;
            // 采样数据总数,即数据总大小-44
            data.setUint32(offset, dataLength, true); offset += 4;
            // 写入采样数据
            if (sampleBits === 8) {
                for (var i = 0; i < bytes.length; i++, offset++) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
                    val = parseInt(255 / (65535 / (val + 32768)));
                    data.setInt8(offset, val, true);
                }
            } else {
                for (var i = 0; i < bytes.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
            }
 
            return new Blob([data], { type: ‘audio/wav’ });
        }
    };
 
    this.start = function () {
        audioInput.connect(recorder);
        recorder.connect(context.destination);
    }
 
    this.stop = function () {
        recorder.disconnect();
    }
 
    this.getBlob = function () {
        return audioData.encodeWAV();
    }
 
    this.clear = function() {
        audioData.clear();
    }
 
    recorder.onaudioprocess = function (e) {
        audioData.input(e.inputBuffer.getChannelData(0));
    }
};
 
SRecorder.get = function (callback) {
    if (callback) {
        if (navigator.getUserMedia) {
            navigator.getUserMedia(
                { audio: true },
                function (stream) {
                    var rec = new SRecorder(stream);
                    callback(rec);
                })
        }
    }
}
 
function receive(e) {
    audio.src = window.URL.createObjectURL(e);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
var a = document.getElementById(‘a’);
var b = document.getElementById(‘b’);
var c = document.getElementById(‘c’);
 
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
 
var gRecorder = null;
var audio = document.querySelector(‘audio’);
var door = false;
var ws = null;
 
b.onclick = function() {
    if(a.value === ”) {
        alert(‘请输入用户名’);
        return false;
    }
    if(!navigator.getUserMedia) {
        alert(‘抱歉您的设备无法语音聊天’);
        return false;
    }
 
    SRecorder.get(function (rec) {
        gRecorder = rec;
    });
 
    ws = new WebSocket("wss://x.x.x.x:8888");
 
    ws.onopen = function() {
        console.log(‘握手成功’);
        ws.send(‘user:’ + a.value);
    };
 
    ws.onmessage = function(e) {
        receive(e.data);
    };
 
    document.onkeydown = function(e) {
        if(e.keyCode === 65) {
            if(!door) {
                gRecorder.start();
                door = true;
            }
        }
    };
 
    document.onkeyup = function(e) {
        if(e.keyCode === 65) {
            if(door) {
                ws.send(gRecorder.getBlob());
                gRecorder.clear();
                gRecorder.stop();
                door = false;
            }
        }
    }
}
 
c.onclick = function() {
    if(ws) {
        ws.close();
    }
}
 
var SRecorder = function(stream) {
    config = {};
 
    config.sampleBits = config.smapleBits || 8;
    config.sampleRate = config.sampleRate || (44100 / 6);
 
    var context = new AudioContext();
    var audioInput = context.createMediaStreamSource(stream);
    var recorder = context.createScriptProcessor(4096, 1, 1);
 
    var audioData = {
        size: 0          //录音文件长度
        , buffer: []     //录音缓存
        , inputSampleRate: context.sampleRate    //输入采样率
        , inputSampleBits: 16       //输入采样数位 8, 16
        , outputSampleRate: config.sampleRate    //输出采样率
        , oututSampleBits: config.sampleBits       //输出采样数位 8, 16
        , clear: function() {
            this.buffer = [];
            this.size = 0;
        }
        , input: function (data) {
            this.buffer.push(new Float32Array(data));
            this.size += data.length;
        }
        , compress: function () { //合并压缩
            //合并
            var data = new Float32Array(this.size);
            var offset = 0;
            for (var i = 0; i < this.buffer.length; i++) {
                data.set(this.buffer[i], offset);
                offset += this.buffer[i].length;
            }
            //压缩
            var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
            var length = data.length / compression;
            var result = new Float32Array(length);
            var index = 0, j = 0;
            while (index < length) {
                result[index] = data[j];
                j += compression;
                index++;
            }
            return result;
        }
        , encodeWAV: function () {
            var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
            var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
            var bytes = this.compress();
            var dataLength = bytes.length * (sampleBits / 8);
            var buffer = new ArrayBuffer(44 + dataLength);
            var data = new DataView(buffer);
 
            var channelCount = 1;//单声道
            var offset = 0;
 
            var writeString = function (str) {
                for (var i = 0; i < str.length; i++) {
                    data.setUint8(offset + i, str.charCodeAt(i));
                }
            };
 
            // 资源交换文件标识符
            writeString(‘RIFF’); offset += 4;
            // 下个地址开始到文件尾总字节数,即文件大小-8
            data.setUint32(offset, 36 + dataLength, true); offset += 4;
            // WAV文件标志
            writeString(‘WAVE’); offset += 4;
            // 波形格式标志
            writeString(‘fmt ‘); offset += 4;
            // 过滤字节,一般为 0x10 = 16
            data.setUint32(offset, 16, true); offset += 4;
            // 格式类别 (PCM形式采样数据)
            data.setUint16(offset, 1, true); offset += 2;
            // 通道数
            data.setUint16(offset, channelCount, true); offset += 2;
            // 采样率,每秒样本数,表示每个通道的播放速度
            data.setUint32(offset, sampleRate, true); offset += 4;
            // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
            data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
            // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
            data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
            // 每样本数据位数
            data.setUint16(offset, sampleBits, true); offset += 2;
            // 数据标识符
            writeString(‘data’); offset += 4;
            // 采样数据总数,即数据总大小-44
            data.setUint32(offset, dataLength, true); offset += 4;
            // 写入采样数据
            if (sampleBits === 8) {
                for (var i = 0; i < bytes.length; i++, offset++) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    var val = s < 0 ? s * 0x8000 : s * 0x7FFF;
                    val = parseInt(255 / (65535 / (val + 32768)));
                    data.setInt8(offset, val, true);
                }
            } else {
                for (var i = 0; i < bytes.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
            }
 
            return new Blob([data], { type: ‘audio/wav’ });
        }
    };
 
    this.start = function () {
        audioInput.connect(recorder);
        recorder.connect(context.destination);
    }
 
    this.stop = function () {
        recorder.disconnect();
    }
 
    this.getBlob = function () {
        return audioData.encodeWAV();
    }
 
    this.clear = function() {
        audioData.clear();
    }
 
    recorder.onaudioprocess = function (e) {
        audioData.input(e.inputBuffer.getChannelData(0));
    }
};
 
SRecorder.get = function (callback) {
    if (callback) {
        if (navigator.getUserMedia) {
            navigator.getUserMedia(
                { audio: true },
                function (stream) {
                    var rec = new SRecorder(stream);
                    callback(rec);
                })
        }
    }
}
 
function receive(e) {
    audio.src = window.URL.createObjectURL(e);
}

注意:按住a键说话,放开a键发送

注意:按住a键说话,放开a键发送

注意:按住a键说话,放开a键发送

协调有尝试不按键实时对讲,通过setInterval发送,但发现杂音有点重,效果不佳,这些要求encodeWAV再一层的包装,多去除环境杂音的效应,自己选用了尤其简便易行的按键说话的形式

协调有品味不按键实时对讲,通过setInterval发送,但意识杂音有点重,效果糟糕,这几个需求encodeWAV再一层的包装,多去除环境杂音的职能,自己挑选了越来越便民的按键说话的方式

和谐有尝试不按键实时对讲,通过setInterval发送,但发现杂音有点重,效果倒霉,那一个必要encodeWAV再一层的包裹,多去除环境杂音的效益,自己挑选了越来越方便的按键说话的形式

 

 

 

那篇小说里首先展望了websocket的前途,然后根据规范大家和好尝尝解析和转移数据帧,对websocket有了更深一步的摸底

那篇小说里第一展望了websocket的前景,然后根据标准大家协调尝试解析和扭转数据帧,对websocket有了更深一步的刺探

这篇小说里首先展望了websocket的以后,然后根据专业大家团结一心尝试解析和浮动数据帧,对websocket有了更深一步的打听

最后通过七个demo看到了websocket的潜力,关于语音聊天室的demo涉及的较广,没有接触过奥迪(Audi)oContext对象的同校最好先了然下奥迪oContext

最后经过四个demo看到了websocket的潜力,关于语音聊天室的demo涉及的较广,没有接触过奥迪oContext对象的校友最好先明白下奥迪oContext

末段通过多少个demo看到了websocket的潜力,关于语音聊天室的demo涉及的较广,没有接触过奥迪oContext对象的同窗最好先领悟下奥迪oContext

作品到此处就终止啦~有哪些想法和问题欢迎大家指出来一起谈论探索~

小说到那里就为止啦~有哪些想法和问题欢迎我们提议来一起商讨探索~

作品到此地就甘休啦~有啥样想法和问题欢迎大家提议来一起谈论探索~

 

 

 

1 赞 11 收藏 3
评论

1 赞 11 收藏 3
评论

1 赞 11 收藏 3
评论

网站地图xml地图