您的位置:首页 > Web前端 > Node.js

【深入浅出Node.js系列十五】Nodejs实现websocket的4种方式

2016-05-27 16:41 1021 查看
WebSocket是HTML5开始提供的
一种浏览器与服务器间进行全双工通讯的网络技术
。在WebSocket API中,浏览器和服务器只需要要做一个握手(handshaking)的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

WebSocket是一个通信的协议,分为服务器和客户端。服务器放在后台,保持与客户端的长连接,完成双方通信的任务。客户端一般都是实现在支持HTML5浏览器核心中,通过提供Javascript API使用网页可以建立websocket连接。


1 为什么用Nodejs

事件驱动,通过闭包很容易实现客户端的生命活期。
不用担心多线程,锁,并行计算的问题
V8引擎速度非常快
对于游戏来说,写一遍游戏逻辑代码,前端后端通用。

当然Nodejs也有一些缺点:
nodejs更新很快,可能会出现版本联兼容
nodejs还不算成熟,还没有大制作。
nodejs不像其他的服务器,对于不同的连接,不支持进程和线程操作。


2 WebSocket-Node

WebSocket-Node,是一个简单的库,不仅支持draft-10,还有之前的各种版本。项目地址:https://github.com/Worlize/WebSocket-Node

服务器端配置:
localhost:king-node TaoBangren$ npm install websocket

> websocket@1.0.22 install /Users/TaoBangren/git@osc/king-node/node_modules/websocket
> (node-gyp rebuild 2> builderror.log) || (exit 0)

CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
SOLINK_MODULE(target) Release/bufferutil.node
CXX(target) Release/obj.target/validation/src/validation.o
SOLINK_MODULE(target) Release/validation.node
king-node@0.0.0 /Users/TaoBangren/git@osc/king-node
└── websocket@1.0.22  extraneous


增加app.js:
~ vi app.js

// http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ "use strict";

// Optional. You will see this name in eg. 'ps' or 'top' command
process.title = 'node-chat';

// Port where we'll run the websocket server
var webSocketsServerPort = 3001;

// websocket and http servers
var webSocketServer = require('websocket').server;
var http = require('http');

/**
* Global variables
*/
// latest 100 messages
var history = [ ];
// list of currently connected clients (users)
var clients = [ ];

/**
* Helper function for escaping input strings
*/
function htmlEntities(str) {
return String(str).replace(/&/g, '&').replace(/>/g, '>').replace(/"/g, '"');
}

// Array with some colors
var colors = [ 'red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange' ];
// ... in random order
colors.sort(function(a,b) { return Math.random() > 0.5; } );

/**
* HTTP server
*/
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
});

/**
* WebSocket server
*/
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server. WebSocket request is just
// an enhanced HTTP request. For more info http://tools.ietf.org/html/rfc6455#page-6 httpServer: server
});

// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
console.log((new Date()) + ' Connection from origin ' + request.origin + '.');

// accept connection - you should check 'request.origin' to make sure that
// client is connecting from your website
// (http://en.wikipedia.org/wiki/Same_origin_policy)
var connection = request.accept(null, request.origin);
// we need to know client index to remove them on 'close' event
var index = clients.push(connection) - 1;
var userName = false;
var userColor = false;

console.log((new Date()) + ' Connection accepted.');

// send back chat history
if (history.length > 0) {
connection.sendUTF(JSON.stringify( { type: 'history', data: history} ));
}

// user sent some message
connection.on('message', function(message) {
if (message.type === 'utf8') { // accept only text
if (userName === false) { // first message sent by user is their name
// remember user name
userName = htmlEntities(message.utf8Data);
// get random color and send it back to the user
userColor = colors.shift();
connection.sendUTF(JSON.stringify({ type:'color', data: userColor }));
console.log((new Date()) + ' User is known as: ' + userName
+ ' with ' + userColor + ' color.');

} else { // log and broadcast the message
console.log((new Date()) + ' Received Message from '
+ userName + ': ' + message.utf8Data);

// we want to keep history of all sent messages
var obj = {
time: (new Date()).getTime(),
text: htmlEntities(message.utf8Data),
author: userName,
color: userColor
};
history.push(obj);
history = history.slice(-100);

// broadcast message to all connected clients
var json = JSON.stringify({ type:'message', data: obj });
for (var i=0; i < clients.length; i++) {
clients[i].sendUTF(json);
}
}
}
});

// user disconnected
connection.on('close', function(connection) {
if (userName !== false && userColor !== false) {
console.log((new Date()) + " Peer "
+ connection.remoteAddress + " disconnected.");
// remove user from the list of connected clients
clients.splice(index, 1);
// push back user's color to be reused by another user
colors.push(userColor);
}
});
});


启动服务器:
~ D:\workspace\javascript\nodejs-websocket>node app.js
Warning: Native modules not compiled.  XOR performance will be degraded.
Warning: Native modules not compiled.  UTF-8 validation disabled.
Wed Aug 21 2013 15:28:48 GMT+0800 (中国标准时间) Server is listening on port 3001
Wed Aug 21 2013 15:28:53 GMT+0800 (中国标准时间) Connection from origin null.
Wed Aug 21 2013 15:28:53 GMT+0800 (中国标准时间) Connection accepted.
Wed Aug 21 2013 15:28:53 GMT+0800 (中国标准时间) User is known as: Conan with red color.
Wed Aug 21 2013 15:28:53 GMT+0800 (中国标准时间) Peer undefined disconnected.


我们看到XOR 和UTF-8 validation 两个模块是被禁止的,不过不影响测试。

客户端,还是使用一样的。





另外,WebSocket-Node还提供C/S模式的交互,可以不使用浏览器:

服务端代码:
~ vi app2.js

#!/usr/bin/env node
var WebSocketServer = require('websocket').server;
var http = require('http');

var server = http.createServer(function(request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
server.listen(3001, function() {
console.log((new Date()) + ' Server is listening on port 3001');
});

wsServer = new WebSocketServer({
httpServer: server,
// You should not use autoAcceptConnections for production
// applications, as it defeats all standard cross-origin protection
// facilities built into the protocol and the browser.  You should
// *always* verify the connection's origin and decide whether or not
// to accept it.
autoAcceptConnections: false
});

function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}

wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
// Make sure we only accept requests from an allowed origin
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}

var connection = request.accept('echo-protocol', request.origin);
console.log((new Date()) + ' Connection accepted.');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ' + message.utf8Data);
connection.sendUTF(message.utf8Data);
}
else if (message.type === 'binary') {
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
connection.sendBytes(message.binaryData);
}
});
connection.on('close', function(reasonCode, description) {
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
});
});


客户端代码:
~ vi client.js

#!/usr/bin/env node
var WebSocketClient = require('websocket').client;

var client = new WebSocketClient();

client.on('connectFailed', function(error) {
console.log('Connect Error: ' + error.toString());
});

client.on('connect', function(connection) {
console.log('WebSocket client connected');
connection.on('error', function(error) {
console.log("Connection Error: " + error.toString());
});
connection.on('close', function() {
console.log('echo-protocol Connection Closed');
});
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log("Received: '" + message.utf8Data + "'");
}
});

function sendNumber() {
if (connection.connected) {
var number = Math.round(Math.random() * 0xFFFFFF);
connection.sendUTF(number.toString());
setTimeout(sendNumber, 1000);
}
}
sendNumber();
});

client.connect('ws://localhost:3001/', 'echo-protocol');


程序启动:
~ D:\workspace\javascript\nodejs-websocket>node app2.js
~ D:\workspace\javascript\nodejs-websocket>node client.js






测试成功!!


3 faye-websocket-node

faye-websocket-node,是扩展faye项目而开发的websocket的一个实现。项目地址:https://github.com/faye/faye-websocket-node

服务器端配置:
~ D:\workspace\javascript>mkdir nodejs-faye-websocket
~ D:\workspace\javascript>cd nodejs-faye-websocket
~ D:\workspace\javascript\nodejs-faye-websocket>npm install faye-websocket
npm http GET https://registry.npmjs.org/faye-websocket npm http 304 https://registry.npmjs.org/faye-websocket npm http GET https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.6.1.tgz npm http 200 https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.6.1.tgz npm http GET https://registry.npmjs.org/websocket-driver npm http 200 https://registry.npmjs.org/websocket-driver npm http GET https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.2.2.tgz npm http 200 https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.2.2.tgz faye-websocket@0.6.1 node_modules\faye-websocket
└── websocket-driver@0.2.2

~ vi app.js
var WebSocket = require('faye-websocket'),
http      = require('http');

var server = http.createServer();

server.on('upgrade', function(request, socket, body) {
if (WebSocket.isWebSocket(request)) {
var ws = new WebSocket(request, socket, body);

ws.on('message', function(event) {
ws.send(event.data);
});

ws.on('close', function(event) {
console.log('close', event.code, event.reason);
ws = null;
});
}
});

server.listen(3001);

~ D:\workspace\javascript\nodejs-faye-websocket>node app.js


用网页客户端访问:





测试成功,非常简单!!而且,没有依赖其他的库!!


4 socket.io

项目地址:https://github.com/LearnBoost/socket.io

环境配置:
~ D:\workspace\javascript>express -e nodejs-socketio
~ D:\workspace\javascript>cd nodejs-socketio && npm install
~ D:\workspace\javascript\nodejs-socketio>npm install socket.io


修改app.js配置文件
~ vi app.js

var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);

server.listen(80);

app.get('/', function (req, res) {
res.sendfile(__dirname + '/client/index.html');
});

io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});


增加客户端文件,注意这个的index.html要根据app.js指定的位置”res.sendfile(__dirname + ‘/client/index.html’);”
~ mkdir client
~ vi /client/index.html

<!DOCTYPE html>
<html>
<head>
<title>socket.io</title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1>socket.io</h1>
<p>Welcome to socket.io</p>

<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
</body>
</html>


启动服务器:
~ D:\workspace\javascript\nodejs-socketio>node app.js
info  - socket.io started


打开浏览器: http://localhost





查看服务器日志:
debug - served static content /socket.io.js
debug - client authorized
info  - handshake authorized ZR-xQhsKCCqM03TRHW4b
debug - setting request GET /socket.io/1/websocket/ZR-xQhsKCCqM03TRHW4b
debug - set heartbeat interval for client ZR-xQhsKCCqM03TRHW4b
debug - client authorized for
debug - websocket writing 1::
debug - websocket writing 5:::{"name":"news","args":[{"hello":"world"}]}
{ my: 'data' }
debug - emitting heartbeat for client ZR-xQhsKCCqM03TRHW4b
debug - websocket writing 2::
debug - set heartbeat timeout for client ZR-xQhsKCCqM03TRHW4b
debug - got heartbeat packet
debug - cleared heartbeat timeout for client ZR-xQhsKCCqM03TRHW4b
debug - set heartbeat interval for client ZR-xQhsKCCqM03TRHW4b


测试成功。


5 总结

node-websocket:需要依赖于底层的C++,Python的环境,支持以node做客户端的访问。

faye-websocket-node:是faye软件框架体系的一部分,安装简单,不需要其他依赖库。

socket.io:功能强大,支持集成websocket服务器端和Express3框架与一身。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: