读书人

nowjs跟nodejs实现服务器端与客户端实

发布时间: 2012-09-04 14:19:30 作者: rapoo

nowjs和nodejs实现服务器端与客户端实时数据传输的例子
参考:http://www.bennadel.com/blog/2171-Realtime-Messaging-And-Synchronization-With-NowJS-And-Node-js.htm

先说例子实现的功能。网页上有一图片,图片可以拖动。用浏览器打开多个同一网页,当图片移动时,其它页面的图片会同步移动。例子也展示了用jQuery实现图片的拖动。
测试环境window7,nodejs v0.6.5 分别用ie,firefox,chrome打开http://127.0.0.1:8080/client.html,所有该网页上的图片会同步移动。贴上代码。
server.js端:
需要用sock.io和nowjs第三包,推荐用npm方式安装。nowjs包在window的安装可参考:
http://blog.nowjs.com/running-nowjs-natively-on-windows

// Include the necessary modules.var sys = require("util");var http = require("http");var url = require("url");var path = require("path");var fileSystem = require("fs");// ---------------------- //// ---------------------- //// Create an instance of the HTTP server.var server = http.createServer(    function (request, response) {// Get the requested "script_name". This is the part of the// path after the server_name.        var scriptName = request.url;// Convert the script name (expand-path) to a physical file// on the local file system.        var requestdFilePath = path.join(process.cwd(), scriptName);// Read in the requested file. Remember, since all File I/O// (input and output) is asynchronous in Node.js, we need to// ask for the file to be read and then provide a callback// for when that file data is available.//// NOTE: You can check to see if the file exists *before* you// try to read it; but for our demo purposes, I don't see an// immediate benefit since the readFile() method provides an// error object.        fileSystem.readFile(            requestdFilePath,            "binary",            function (error, fileBinary) {// Check to see if there was a problem reading the// file. If so, we'll **assume** it is a 404 error.                if (error) {// Send the file not found header.                    response.writeHead(404);// Close the response.                    response.end();// Return out of this guard statement.                    return;                }// If we made it this far then the file was read in// without a problem. Set a 200 status response.                response.writeHead(200);// Serve up the file binary data. When doing this, we// have to set the encoding as binary (it defaults to// UTF-8).                response.write(fileBinary, "binary");// End the response.                response.end();            }        );    });// Point the server to listen to the given port for incoming// requests.server.listen(8080);// ---------------------- //// ---------------------- //// Create a local memory space for further now-configuration.(function () {// Now that we have our HTTP server initialized, let's configure// our NowJS connector.    var nowjs = require("now");// After we have set up our HTTP server to serve up "Static"// files, we pass it off to the NowJS connector to have it// augment the server object. This will prepare it to serve up// the NowJS client module (including the appropriate port// number and server name) and basically wire everything together// for us.//// Everyone contains an object called "now" (ie. everyone.now) -// this allows variables and functions to be shared between the// server and the client.    var everyone = nowjs.initialize(server);// Create primary key to keep track of all the clients that// connect. Each one will be assigned a unique ID.    var primaryKey = 0;// When a client has connected, assign it a UUID. In the// context of this callback, "this" refers to the specific client// that is communicating with the server.//// NOTE: This "uuid" value is NOT synced to the client; however,// when the client connects to the server, this UUID will be// available in the calling context.    everyone.connected(        function () {            this.now.uuid = ++primaryKey;        }    );// Add a broadcast function to *every* client that they can call// when they want to sync the position of the draggable target.// In the context of this callback, "this" refers to the// specific client that is communicating with the server.    everyone.now.syncPosition = function (position) {//syncPosition()在这里定义,在客户端调用// Now that we have the new position, we want to broadcast// this back to every client except the one that sent it in// the first place! As such, we want to perform a server-side// filtering of the clients. To do this, we will use a filter// method which filters on the UUID we assigned at connection// time.        everyone.now.filterUpdateBroadcast(this.now.uuid, position);    };// We want the "update" messages to go to every client except// the one that announced it (as it is taking care of that on// its own site). As such, we need a way to filter our update// broadcasts. By defining this filter method on the server, it// allows us to cut down on some server-client communication.    everyone.now.filterUpdateBroadcast = function (masterUUID, position) {// Make sure this client is NOT the same client as the one// that sent the original position broadcast.        if (this.now.uuid == masterUUID) {// Return out of guard statement - we don't want to// send an update message back to the sender.            return;        }// If we've made it this far, then this client is a slave// client, not a master client.        this.now.updatePosition(position);//updatePosition()为客户端定义的方法,在这里可调用,用this修饰now。    };})();// ---------------------- //// ---------------------- //// Write debugging information to the console to indicate that// the server has been configured and is up and running.sys.puts("Server is running on 8080");


我把重要的东西摘录下来:

This is true for both variables and functions! Notice that my server-side Node.js code defines two methods: syncPosition() and filterUpdateBroadcast(). By defining them in the "everyone.now" scope, I am making them available to both the server and to every single one of the connected clients.

But what about that, "everyone.now.updatePosition()", function? Where did that come from? Ah-ha! Here's the real, "there is no spoon" mind-screw - that function is defined on the client (which we'll see in a minute). And, since it's defined in the client's "now" scope, the server-side Javascript can then invoke it as if there were no separation between the server and client contexts.

client.html
<!DOCTYPE html><html><head>    <title>NowJS And Node.js Realtime Communication</title>    <style type="text/css">        html,        body {            height: 100%;            overflow: hidden;            width: 100%;        }        img {            left: 9px;            position: absolute;            top: 70px;        }    </style>    <!-- We have this file stored explicitly. -->    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"/>    </script>    <!--    The NowJS HTTP augmentation will take care of routing    this - we don't actually have this physical file stored    at this file path.    -->    <script type = "text/javascript" src = "/nowjs/now.js" ></script></head><body><h1>    NowJS And Node.js Realtime Communication</h1><!--This will be draggable. When this image drags, we aregoing to sync the position of it across browsers.--><img        id="myhead"        src="./myhead.gif"        width="100"        height="100"        alt="nowjs跟nodejs实现服务器端与客户端实时数据传输的例子"        /><!-- Configure the client-side script. --><script type="text/javascript">    // Get a reference to the target draggable.    var myhead = $("#myhead");    // Get a reference to the body - this is the element on which    // we'll be tracking mouse movement once the draggable    // tracking has been turned on.    var body = $("body");    // On mouse-down, turn on draggability.    myhead.mousedown(            function (event) {// Prevent the default behavior.                event.preventDefault();// Get the current position of the mouse within the// bounds of the target.                var localOffset = {                    x:(event.pageX - myhead.position().left),                    y:(event.pageY - myhead.position().top)                };// Start tracking the mouse movement on the body.// We're tracking on the body so that the mouse can// move faster than the tracking.                body.mousemove(                        function (event) {// Create a new position object.                            var newPosition = {                                left:(event.pageX - localOffset.x),                                top:(event.pageY - localOffset.y)                            };// Update the target position locally.                            myhead.css(newPosition);// Announce the updated position so that we// can sync accross all clients with NowJS.                            now.syncPosition(newPosition);//syncPosition()是在服务器端定义的方法,可在客户端调用。                        }                );            }    );    // On mouse-up, turn off draggability.    myhead.mouseup(            function (event) {// Unbind the mousemove - no need to track movement// once the mouse has been lifted.                body.unbind("mousemove");            }    );    // I allow the remove server to make a request to update the    // position of the target.    //    // NOTE: By defining this function in the NOW scope, it gives    // the server access to it as well.    now.updatePosition = function (newPosition){//updatePosition()这个方法在客户端定义,可在服务器端调用// Check to see if this client is in master mode; if so,// we won't update the position as this client is// actively updating its own position.        myhead.css(newPosition);    };</script></body></html>



Even after coding this myself, it's still somewhat confusing; so, let's look at a quick rundown of the various functions to see where they were defined and where they were invoked:

syncPosition()
Defined: Server-sideInvoked: Client-side
filterUpdateBroadcast()
Defined: Server-sideInvoked: Server-side
updatePosition()
Defined: Client-sideInvoked: Server-side

读书人网 >JavaScript

热点推荐