【深入浅出Node.jsシリーズ15】Nodejsがwebsocketを実現する4つの方法

78942 ワード

WebSocketは、HTML 5が提供を開始した です.WebSocket APIでは、ブラウザとサーバが握手(handshaking)するだけで、ブラウザとサーバの間に高速チャネルが形成されます.両者の間で直接データを互いに転送することができる.
WebSocketは、サーバとクライアントに分かれた通信プロトコルです.サーバーはバックグラウンドに置いて、クライアントとの長い接続を維持して、双方の通信の任務を完成します.クライアントは一般的にHTML 5ブラウザをサポートするコアで実現され、Javascript APIを提供してWebページを使用することでwebsocket接続を確立することができます.
1なぜNodejsを使うのか
イベントドライバは、パッケージを閉じることでクライアントのライフサイクルを容易に実現します.
マルチスレッド、ロック、並列計算の問題を心配する必要はありませんV 8エンジンの速度は非常に速いゲームにとって、ゲームの論理コードを1回書いて、フロントエンドのバックエンドは通用します.
もちろん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の2つのモジュールは禁止されていますが、テストには影響しません.
クライアントは、同じものを使用します.
また、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
odejs
-websocket>node app2.js ~ D:\workspace\javascript
odejs
-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
odejs-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
odejs-faye-websocket>node
app.js

Webクライアントでアクセス:
テストに成功して、とても简単です!!しかも、他のライブラリに依存していません!!
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
odejs
-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
odejs-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サーバ側とExpress 3フレームワークの統合をサポートします.