实现功能:将socket.io服务器的二进制图片流利用img标签显示在浏览器端,形成视频。
所用到的知识:WebSocket协议,Node.js的Socket.io类库,浏览器的对象URL机制。
WebSocket协议
WebSocket是HTML5的一种新协议, 它实现了浏览器与服务器全双工通信。以前很多网站为了实现实时通信,所用到的技术都是轮询,轮询是在特定的时间间隔,由浏览器对服务器发出http请求,然后由服务器返回最新的数据给浏览器。很显然,这种传统模式的http请求需要浏览器端不断的发出请求,http请求的header是非常长的,如果只是要发一个很小的数据,会非常浪费带宽。在WebSocket API,浏览器和服务器只需要做一个握手的动作就能实现全双工通讯。
Socket.io类库
浏览器的兼容问题以及网络中间物(代理服务、防火墙)问题不支持WebSocket,这时Socket.io的出现就是为了完善这些问题。Socket.io是一个简单的小类库,该类库实现的功能类似于Node.js中的net模块实现的功能,这些功能包括WebSocket通信、XHR轮询、JSONP轮询等。该类库的一个显著特征是在服务器端与浏览器之间提供一共享接口,也就是说,当客户端与服务端建立连接后,在处理消息时,开发者可以在客户端使用服务器端JavaScript代码。
下面是个小案例:
// socket.js
var http = require('http');
var sio = require('socket.io');
var fs = require('fs');
var server = http.createServer(function(req,res){
res.writeHead(200,{'Content-type':'text/html'});
res.end(fs.readFileSync('./index.html'));
});
server.listen(1337);
var socket = sio.listen(server);
socket.on('connectiion',function(socket){
console.log('客户端建立连接');
socket.send('你好');
socket.on('message',function(msg){
console.log('接收到一个消息:',msg);
});
socket.on('disconnect',function(){
console.log('客户端断开连接');
});
});
// index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="//cdn.bootcss.com/socket.io/1.7.3/socket.io.js"></script>
<script>
var socket = io.connect();
socket.on('message',function(data){
console.log(data);
socket.send('消息已接收到');
});
socket.on('disconnect',function(){
console.log('服务器断开连接');
});
</script>
</head>
<body>
</body>
</html>
浏览器的对象URL机制
对象URL机制--后台服务器直接发送JPEG图像数据的原始二进制数据,浏览器收到数据后把图像数据解析成img元素能过引用的文件,能够有效降低图像数据传输的占用带宽。
objectURL=window.URL.createObjectURL(blob);
blob参数是一个文件对象或者Blob对象,Blob对象代表文件的二进制数据,objectURL是生成的对象URL,这个URL的格式为"blob"开头的链接对象,一个对象的URL长这个样子:
![](blob:http%3A//localhost%3A4000/ffd23149-960f-4555-92a5-c3e1d6e9d1d9)
具体用法参考http://www.cnblogs.com/liulangmao/p/4262565.html
还有一种是对象URI机制,一般而言,img标签无法把图像的二进制数据解析成图片文件,这是由浏览器防止用户直接引用用户计算机本地文件的安全机制决定的,但是浏览器有种解析data URI数据的方式,我们可以利用这种特性来解析图像。data URI是一种直接把文件嵌入HTML文档的方案,其格式如下:
data:[<mediatype>][;base64],<data>
上述<data>部分就是文件原始二进制数据的base64编码数据,但是这种编码得到的数据比原始数据大了1/3,会导致数据传输占用带宽加大,因此,我们采用对象URL机制。
下面是将socket.io服务器的二进制数据传输给浏览器的代码(‘图片的二进制数据可以自己将本地图片的二进制数据输进去’):
var http = require('http');
var sio = require('socket.io');
var fs = require('fs');
var server = http.createServer(function(req,res){
res.writeHead(200,
{
'Content-type':'text/html'
}
);
res.end(fs.readFileSync('./img.html'));
});
server.listen(4000);
var io = sio.listen(server);
socket.on('connection',function(socket){
socket.emit('news',{shuju:'图片的二进制数据'});
})
浏览器端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="//cdn.bootcss.com/socket.io/1.7.3/socket.io.js"></script>
<script>
var socket = io.connect();
socket.on('news',function(data){
var blob = new Blob([data.shuju],{"type":"image\/jpeg"});
var src = window.URL.createObjectURL(blob);
var img = document.createElement('img');
img.src = src;
img.onload = function(){
window.URL.revokeObjectURL(src);
};
document.body.appendChild(img);
});
</script>
</head>
<body>
<div id="data"></div>
</body>
</html>
由于是用本地图片测试的,路径会有问题,会导致浏览器端显示不出来图片可以在控制台查看是否正确:
要想从http服务器传输图片数据给socket.io服务器,可以用node.js的进程和子进程解决,将socket.io代码设为http部分的子进程,即可实现传输。
// http.js
var http = require('http');
var cp = require('child_process');
var n= cp.fork(__dirname+'/socket.js');
var server = http.createServer(function(req,res){
}).listen(3000,"localhost",function(){
n.send({shuju:'图片二进制数据'})});
//监听事件
server.on('listening',function(){
console.log('服务器开始监听');
//server.close();
});
//建立连接
server.on('connection',function(socket){
console.log('客户端已连接');
});
//服务器关闭时响应
server.on('close',function(){
console.log('服务器已关闭');
});
//触发错误事件
server.on('error',function(){
if(e.code=='EADDRINUSE'){ //端口被占用的错误代码为EADDRINUSE
console.log('服务器端口已被占用');
}
});
由于JPEG编码得到的图像数据大小均不同,且TCP协议只是保证接收端接受的的比特是按序的,但不能保证每次接收的数据的就是发送端发送的完整数据,可能只是其中的一部分。因此,node.js服务器接收时不能用长度来指定接收一帧JPEG图像数据的Buffer。此处使用了JPEG数据的最后两个ASCII码的217和255来区分两帧图像数据,并使用Buffer把之前接收到的二进制数据缓存起来最后拼接完成完整的JPEG帧数据。
socket.js
var http = require('http');
var sio = require('socket.io');
var fs = require('fs');
var server = http.createServer(function(req,res){
res.writeHead(200,
{
'Content-type':'text/html'
}
);
res.end(fs.readFileSync('./img.html'));
});
server.listen(3000);
var io = sio.listen(server);
process.on('message',function(m){
var buffers=[];
io.sockets.on('connection',function(socket){
buffers.push[m.shuju];
var n_shu= m.shuju;
if(n_shu[n_shu.length-1]==217 && n_shu[n_shu.length-2]==255){
var n_buffer = Buffer.concat(buffers);
io.sockets.emit('news',m);
buffers=[];
}
});
});
浏览器端代码不变,以上便完成图像的传输。