websocket与socket.io的那些事儿
1、WebSocket
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器的全双工(full-duplex)通信模式——可以通俗的解释为服务器主动发送信息给客户端。是HTML5规范中的新特性.
优点:它提供了单个tcp连接上的双向通信.
由于WebSocket是真正意义上的双向通信(与轮询,阻塞模拟长连接不同),它完全减少了频繁的身份验证等不必要的开销。WebSocket可以看作HTTP上的一个扩展,用到了HTTP的内容,但是和HTTP协议有着本质上的不同。
在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” (handshaking)。在WebSocket API,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket协议中,为我们实现即时服务带来了两大好处:
- Header
互相沟通的Header是很小的-大概只有2 Bytes
- Server Push
服务器的推送,服务器不再被动的接收到浏览器的request之后才返回数据,而是在有新数据时就主动推送给浏览器。
浏览器请求:
GET /webfin/websocket/ HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
服务器回应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
WebSocket借用http请求进行握手,相比正常的http请求,多了一些内容。其中,
Upgrade: websocket
Connection: Upgrade
表示希望将http协议升级到Websocket协议。
Sec-WebSocket-Key是浏览器随机生成的base64 encode的值,用来询问服务器是否是支持WebSocket。
服务器返回
Upgrade: websocket
Connection: Upgrade
告诉浏览器即将升级的是Websocket协议
Sec-WebSocket-Accept是将请求包“Sec-WebSocket-Key”的值,与”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″这个字符串进行拼接,然后对拼接后的字符串进行sha-1运算,再进行base64编码得到的。用来说明自己是WebSocket助理服务器。
Sec-WebSocket-Version是WebSocket协议版本号。RFC6455要求使用的版本是13,之前草案的版本均应当被弃用。
浏览器验证通过数据包内容、格式,即成功建立websocket协议(状态码101)。
websocket必须浏览器要支持,服务器要支持,数据传输格式是Frame.
创建一个websocket 实例:
websocket = new Websocket("ws:localhost/demo");
2、Socket.io
Socket.io:
介绍:Socket.IO是一个完全由JavaScript实现、基于Node.js、支持WebSocket的协议用于实时通信、跨平台的开源框架,它包括了客户端的JavaScript和服务器端的Node.js。
使用场景:Socket.IO设计的目标是构建能够在不同浏览器和移动设备上良好运行的实时应用,如实时分析系统、二进制流数据处理应用、在线聊天室、在线客服系统、评论系统、WebIM等。
特点:
1.Socket.IO已经具有众多强大功能的模块和扩展API
2.Socket.IO实现了实时、双向、基于事件的通讯机制,它解决了实时的通信问题,并统一了服务端与客户端的编程方式。启动了Socket以后,就像建立了一条客户端与服务端的管道,两边可以互通有无。它还能够和Express.js提供的传统请求方式很好的结合,即可以在同一个域名,同一个端口提供两种连接方式:
安装socket.io:
搭建Socket.IO环境需要先创建一个作为工作空间的目录,然后安装Node.js,并在工作空间下安装Socket.IO(命令:npm install socket.io),这样环境已经搭建完成。还可以安装基于Node.js框架(如Express.js等)以协助应用的服务器端开发。
npm install socketio express --save-dev
socketio是对websocket协议封装的一个模块,让客户端和服务器端的写法都是一样的
socket.emit()发数据
socket.on()收数据
常用方法:
监听客户端连接,回调函数会传递本次连接的socket
io.on('connection',function(socket));
给所有客户端广播消息
io.sockets.emit('String',data);
给指定的客户端发送消息
io.sockets.socket(socketid).emit('String', data);
监听客户端发送的信息
socket.on('String',function(data));
给该socket的客户端发送消息
socket.emit('String', data);
下面给一个服务端模拟登入实例(客户端类似只需求引入socketio.js文件就可以了):
var sio = require('socket.io')(port);
var timer = null; // 定时器
var interval = 1000; // 时间间隔,1s
/**************************************************************
* SOCKET.IO 连接管理服务
**************************************************************/
// 监听socket.io
sio.sockets.on('connection', function(socket){
var address = socket.handshake.address;
console.log('New client: ' + address);
// 启动给所有客户端广播的定时器
startTimer(interval);
// 连接到服务器:auth(uid/token/imei){ret/msg/}
socket.on('auth', function(data){
console.log('--auth:' + JSON.stringify(data));
if(data == null) {
return;
}
if (data.uid == '' || data.token == '' ){
return;
}
// 已经登录过了
if(socket.uid != null){
return;
}
// 验证用户的合法性
//if(!signMgr.checkSign(uid, sign)){
// socket_return(socket, 'auth_result', -1, '无效的签名');
// socket.disconnect();
// return;
//}
socket.uid = data.uid;
socket.token = data.token;
// 放入缓存
pool[socket.uid] = socket;
// 测试显示
show_pool();
});
// 切换跟踪设备 auth(){ret/msg/}
socket.on('switch', function(data){
console.log('--switch:' + JSON.stringify(data));
if(data == null) {
socket_return(socket, 'switch_result', -1, 'invalid json request', data);
return;
}
});
// 终端断开连接
socket.on('disconnect', function(data){
console.log('--disconnect:' + JSON.stringify(data));
// 从缓冲池中移除
var uid = socket.uid;
delete pool[uid];
show_pool();
});
});
console.log("follow server is running. port:" + port);
/**************************************************************
* 定时任务,项所有的终端发送信息
**************************************************************/
function startTimer(delay){
timer = setInterval(broadcast, delay);
}
function stopTimer(){
if (timer){
clearInterval(timer);
timer = null;
}
}
// 显示缓冲池内的内容
function show_pool(){
console.log('--------------pool-------------');
for(var uid in pool){
var socket = pool[uid];
}
console.log('-------------------------------');
}
/**
* @brief socket响应
* */
function socket_return(socket, event, ret, msg, data){
if (data == undefined){
data = {};
}
socket.emit(event, {ret:ret, msg:msg, data:data});
}
// 时间广播
function broadcast(){
for(var uid in pool){
var socket = pool[uid];
if (socket == null){
console.log('pool.socket is empty:' + uid);
continue;
}
//返回实时数据
socket.emit('gps', {uid:uid,name:name});
}
}