node.js 是一個可以使用 JavaScript 開發服務器端應用的平臺。它依托于 Google V8 JavaScript 引擎,并采用事件 I/O 的架構,可以用來創建高性能服務器。本文詳細介紹了 node.js 的基本知識、模塊化的結構、事件驅動的機制以及常用的模塊。
?
隨著 Web 2.0 概念和 Ajax 技術的流行,JavaScript 作為 Ajax 應用開發中必不可少的一部分,已經得到了廣泛的流行。開發人員也開始逐步的熟悉和掌握 JavaScript,并積累了相關的開發經驗。雖然 JavaScript 目前主要用在 Web 應用中,以瀏覽器作為運行平臺,但是已經有相關的嘗試把 JavaScript 遷移到服務器端,這其中包括 Aptana 的 Jaxer 等。這種做法與 Google GWT 是異曲同工的。Google GWT 允許開發人員使用 Java 語言來編寫 Web 前端代碼。這兩種做法的目的都是為了復用開發人員已經掌握的知識和積累的經驗。在這點上,node.js 類似于 Jaxer。
?
簡 單的來說,node.js 是一個允許開發人員使用 JavaScript 語言編寫服務器端代碼的框架。也就是說編寫的 JavaScript 代碼可以直接運行在本地機器上,而不僅限于瀏覽器。從實現的角度來說,Jaxer 和 node.js 都使用了已有的 JavaScript 執行引擎。Jaxer 用的是 Mozilla Firefox 中使用的 JavaScript 引擎,而 node.js 用的則是 Google Chrome 中用的 V8 引擎。
?
node.js 入門
node.js 可以運行在 Linux、Windows 和 Macintosh 等主流的操作系統上。在 Windows 平臺上運行 node.js 的話,需要 Cygwin 或是 MinGW 的支持。下面以常用的 Windows 平臺為例來說明。首先需要安裝 Cygwin。安裝的時候需要選擇
gcc-g++
、
make
、
openssl
和
python
等包。
gcc
的版本必須是最新的。接著從
參考資料
中給出的地址下載 node.js 0.4.0 版本的源代碼。下載解壓之后,依次在 Cygwin 中運行
./configure
、
make
和
make install
等命令進行編譯和安裝。安裝完成之后,直接運行
node
命令就可以啟動 node.js 提供的命令行。在命令行中可以直接輸入 JavaScript 代碼并運行。也可以通過
node server.js
的方式來運行一個 JavaScript 文件
server.js
。
?
代碼清單 1
中給出了一個簡單的“Hello World”程序的示例。通過
node helloworld.js
來運行該 JavaScript 文件之后,會在控制臺輸出“Hello World”。
?
清單 1. 使用 node.js 的“Hello World”程序
process.stdout.write("Hello World");
?
?代碼清單 1
中的
process
表示的是當前運行的 node.js 進程,其屬性
stdout
表示的是進程的標準輸出流。通過
write()
方法向給流中寫入一個字符串。從
代碼清單 1
可以看到,使用 JavaScript 就可以訪問標準輸出流等本地系統上的資源。這從一個側面反映出來了 node.js 的強大。
?
在 node.js 可以運行的 JavaScript 代碼中,可以使用一些全局的對象:包括
代碼清單 1
中用到的
process
、下面會介紹的用來加載模塊的
require()
方法、表示當前正在執行的 JavaScript 文件名的
__filename
、表示當前正在執行的 JavaScript 文件的目錄的
__dirname
和與瀏覽器中相似的用來執行定時任務的
setTimeout()
和
setInterval()
方法等。
?
在介紹了 node.js 的基本知識之后,下面介紹 node.js 的模塊化結構。
?
模塊化結構
node.js 使用了 CommonJS 定義的模塊系統。不同的功能組件被劃分成不同的模塊。應用可以根據自己的需要來選擇使用合適的模塊。每個模塊都會暴露一些公共的方法或屬性。模塊使用者直 接使用這些方法或屬性即可,不需要關系模塊內部的實現細節。除了系統預置的多個模塊之外,應用開發團隊也可以利用這個機制來將應用拆分成多個模塊,以提高 代碼的可復用性。
?
使用模塊
在 node.js 中使用一個模塊的方式是非常簡單的。使用某個模塊之前需要首先聲明對它的依賴。在 JavaScript 代碼中可以直接使用全局函數
require()
來加載一個模塊。如
require("http")
可以加載系統預置的
http
模塊。而
require("./myModule.js")
用來加載與當前 JavaScript 文件同一目錄下的
myModule.js
模塊。如果使用
require()
的路徑以“/”開頭的話,則認為是模塊 JavaScript 文件在操作系統上的絕對路徑。如果不是這兩種情況的話,node.js 就會嘗試在當前 JavaScript 文件的父目錄及其祖先目錄下的
node_modules
目錄下查找。比如目錄
/usr/home/my.js
中調用了
require("other.js")
的話,node.js 會依次嘗試查找下列文件:
/usr/home/node_modules/other.js
、
/usr/node_modules/other.js
和
/node_modules/other.js
。
?
require()
方法的返回值是該模塊所暴露出來的公開 JavaScript 對象,包含了可供使用的方法和屬性。
代碼清單 2
給出了模塊的基本使用方式。
?
清單 2. 模塊的基本使用方式
var greetings = require("./greetings.js"); var msg = greetings.sayHello("Alex", "zh_CN"); process.stdout.write(msg);
?
如
代碼清單 2
所示,一般是直接把
require()
方法的返回值賦值給一個變量,在 JavaScript 代碼中直接使用此變量即可。
greetings.js
模塊暴露了一個
sayHello()
方法,當前 JavaScript 代碼直接使用了該方法。
?
開發自己的模塊
開發自己的模塊的基本工作是在模塊對應的 JavaScript 文件中編寫模塊相關的代碼。這其中封裝了模塊的內部處理邏輯。一般來說,一個模塊通常會暴露一些公開的方法或屬性給其使用者。模塊的內部代碼需要把這些方法或屬性給暴露出來。
代碼清單 3
給出了
代碼清單 2
中所使用的
greetings.js
文件的內容。
?
清單 3. greetings.js 模塊的內容
var languages = { "zh_CN" : "你好,", "en" : "Hello, " }; exports.sayHello = function(name, language) { return languages[language] || languages["en"] + name; };
?
如
代碼清單 3
所示,
exports
對象的內容就是模塊的使用者調用
require()
方法的返回值中所包含的內容。模塊通過這種方式來聲明其所暴露出來的公開方法和屬性。在模塊中定義的變量,如
languages
,是只對模塊內部的代碼可見的。
?
如果一個模塊所包含的內容比較多,也可以用文件夾的方式來組織??梢栽谖募A的根目錄下面創建一個
package.json
文件,其內容中包含了模塊的名稱和入口 JavaScript 文件的路徑。如果沒有提供這個
package.json
文件的話,node.js 會默認在文件夾中查找
index.js
文件作為模塊的啟動 JavaScript 文件。
?
在介紹完 node.js 的模塊化結構之后,下面介紹其事件驅動機制。
?
事件驅動
開發過 Web 應用的人都熟悉瀏覽器中的事件處理機制。當對某個 DOM 元素上的某類事件感興趣的時候,只需要在該 DOM 元素上面注冊一個事件監聽器即可。如
ele.addEventListener("click", function() {})
就添加了一個對
click
事件的監聽器。當事件發生的時候,事件監聽器的 JavaScript 方法就會被調用。事件的處理方法是異步執行的。這種異步執行的方式非常適合于開發高性能并發網絡應用。實際上,目前的高性能并發應用開發一般有兩種做法: 第一種是使用多線程的機制,另外一種就是采用基于事件驅動的方式。多線程的問題在于應用開發起來難度較高,很容易出現線程饑餓或是死鎖等問題,對開發人員 提出了更高的要求。而事件驅動的方式則更加靈活,很容易為 Web 開發人員所理解和使用,也不存在線程死鎖等問題。依托于性能強大的 Google V8 引擎和先進的事件 I/O 架構,node.js 可以成為創建高性能服務器端應用的良好基礎。
?
基于 node.js 開發應用與開發 Web 應用有相似的編程模型。很多模塊都會暴露出一些事件,使用這些模塊的代碼通過注冊事件監聽器的方式來添加相應的處理邏輯。 代碼清單 4 中給出了一個簡單的 HTTP 代理服務器的實現代碼。
?
清單 4. HTTP 代理服務器
var http = require("http"); var url = require("url"); http.createServer(function (req, res) { var urlObj = url.parse(req.url, true); // 獲取被代理的 URL var urlToProxy = urlObj.query.url; if (!urlToProxy) { res.statusCode = 400; res.end("URL 是必須的。"); } else { console.log("處理代理請求:" + urlToProxy); var parsedUrl = url.parse(urlToProxy); var opt = { host : parsedUrl.hostname, port : parsedUrl.port || 80, path : (parsedUrl.pathname || "") + (parsedUrl.search || "") + (parsedUrl.hash || "") }; http.get(opt, function(pres) { // 請求被代理 URL 的內容 res.statusCode = pres.statusCode; var headers = pres.headers; for (var key in headers) { res.setHeader(key, headers[key]); } pres.on("data", function(chunk) { res.write(chunk); // 寫回數據 }); pres.on("end", function() { res.end(); }); }); } }).listen(8088, "127.0.0.1"); console.log("代理服務器已經在 8088 端口啟動。");
?
整個代理服務器的實現比較簡單。首先通過
http
模塊中的
createServer()
方法用來創建一個 HTTP 服務器,再通過
listen()
方法就可以讓該 HTTP 服務器在特定端口監聽。在
createServer()
方法中傳入的參數是 HTTP 請求的響應方法。實際上,每個 HTTP 請求都是對應于 HTTP 服務器上的一個
request
事件。
代碼清單 4
中的 HTTP 服務器創建部分實際上等價于
代碼清單 5
中給出的實現方式。
?
清單 5. 使用事件機制的 HTTP 服務器創建方式
var server = http.createServer(); server.on("request", function(req, res) { });
?
在請求的處理方法里面,通過
http.get()
方法來獲取被代理 URL 的內容。這里同樣采用了基于事件的處理方式。
pres.on("data", function(chunk) {})
在
pres
的
data
事件上添加了一個處理方法。該方法的作用是當獲取到被代理 URL 的內容的時候,就把獲取到的內容寫回到原始 HTTP 請求的響應中。對于
end
事件的處理也是同樣的。在使用 node.js 進行開發的時候,會經常遇到這種使用事件處理方法和回調方法的場景。
?
在介紹了 node.js 的事件驅動機制之后,下面介紹一些常用的模塊。
?
常用模塊
node.js 默認提供了很多與網絡與文件系統操作相關的模塊。這些模塊是構建服務器端應用的基礎。下面對其中一些常見的模塊進行具體說明。
?
事件模塊
前面提到過,node.js 采用的是事件驅動的架構,其中的很多模塊都會產生各種不同的事件,可以由模塊使用者來添加事件處理方法。所有能夠產生事件的對象都是事件模塊中的
EventEmitter
類的實例。
?
EventEmitter
類中的方法都與事件的產生和處理相關,如下所示:
-
addListener(event, listener)
和on(event, listener)
:這兩個方法的作用都是用來為某個事件event
添加事件處理方法listener
。 -
once(event, listener)
:這個方法為某個事件event
添加僅執行一次的處理方法listener
。處理方法在執行一次之后就會被刪除。 -
removeListener(event, listener)
:該方法用來刪除某個事件event
上的處理方法listener
。 -
emit(event, [arg1], [arg2], [...])
:該方法用來產生某個事件event
。事件名稱event
之后的參數被傳遞給對應的事件處理方法。
代碼清單 6 給出了事件模塊的使用示例。
?
清單 6. 事件模塊的使用示例
var events = require("events"); var emitter = new events.EventEmitter(); emitter.on("myEvent", function(msg) { console.log(msg); }); emitter.emit("myEvent", "Hello World.");
?
在事件模塊中有一個特殊的事件
error
。當出現錯誤的時候,
EventEmitter
會產生此事件。如果此事件沒有對應的處理方法的話,默認的行為是輸出錯誤信息后,程序自動終止。因此,需要注意總是添加一個對
error
事件的處理方法。
?
流
node.js 中存在各式各樣不同的數據流,包括文件系統、HTTP 請求和響應、以及 TCP/UDP 連接等。這些流都是
EventEmitter
的實例,因此可以產生各種不同的事件。流可以分成只讀、只寫和讀寫流三種。
?
可讀流主要會產生 4 個事件:
-
data
:當讀取到流中的數據時,產生此事件。 -
end
:當流中沒有數據可讀時,產生此事件。 -
error
:當讀取數據出現錯誤時,產生此事件。 -
close
:當流被關閉時,產生此事件。
除了上面的事件之外,還有一個
pipe()
方法可以用來把當前的可讀流和另外一個可寫流連接起來??勺x流中的數據會被自動寫入到可寫流中。
?
可寫流中最常用的是
write()
和
end()
兩個方法。
write()
方法用來向流中寫入數據,而
end()
則用來結束寫入操作。
?
為了表示二進制數據,node.js 使用了類
Buffer
來表示數據緩沖區,以對二進制數據進行操作。
Buffer
類內部是以數組的方式來存儲數據的。一旦創建出來之后,
Buffer
的大小是不能被修改的。
Buffer
類的實例是可以與 JavaScript 中的字符串類型互相轉換的。在轉換的時候需要指定編碼格式。通過
Buffer
類的
toString(encoding, start, end)
方法可以把
Buffer
中的從
start
到
end
的內容轉換成以
encoding
編碼的字符串。可以支持的編碼格式有:
ascii
、
utf8
和
base64
。通過
new Buffer(str, encoding)
可以用一個字符串
str
來初始化一個緩沖區。
?
write(string, offset, encoding)
用來把一個字符串
string
以編碼格式
encoding
寫入到緩沖區中以
offset
開始的位置上。
?
網絡操作
node.js 提供了一些與網絡操作相關的模塊,包括 TCP、UDP 和 HTTP 等,可以實現網絡服務器和客戶端。
與 TCP 協議相關的實現在
net
模塊中。通過該模塊的
createServer(connectionListener)
方法可以創建一個 TCP 服務器。參數
connectionListener
是當有客戶端連接到該服務器上時的處理方法,等價于對
connect
事件的處理。一個 TCP 服務器是類
Server
的實例。通過
listen
方法可以讓服務器在指定端口監聽。
?
如果想連接到一個已有的 TCP 服務器的話,可以使用
createConnection(port, host)
方法來連接到指定主機
host
的端口
port
上。該方法的返回值是
Socket
類的實例,表示一個套接字連接。得到一個
Socket
類的實例之后,就可以通過
write()
方法來向該連接中寫入數據。如果想從該連接上獲取數據的話,可以添加
data
事件的處理方法。
?
代碼清單 7
中給出了一個簡單的用來進行表達式計算的 TCP 服務器,可以通過
telnet
命令連接到此服務器來進行測試。
?
清單 7. 簡單的表達式計算服務器
var net = require("net"); var server = net.createServer(function(socket) { socket.setEncoding("utf8"); var buffer = [], len = 0; socket.on("data", function(data) { // 接收到客戶端數據 if (data.charCodeAt(0) == 13) { var expr = buffer.join(""); try { var result = eval(expr); // 進行計算 socket.write(result.toString()); // 寫回結果 } catch (e) { socket.write("Wrong expression."); } finally { socket.write("\r\n"); buffer = []; } } else { buffer.push(data); } }); }); server.listen(8180, "127.0.0.1"); console.log("服務器已經在端口 8180 啟動。");
?
除了 TCP 服務器外,模塊
http
和
https
可以分別實現 HTTP 和 HTTPS 服務器,模塊
dgram
可以實現 UDP/Datagram 套接字連接,模塊
tls
可以實現安全的套接字連接(SSL)。這些模塊的使用方式都類似于模塊
tcp
。
?
文件系統
node.js 中的
fs
模塊用來對本地文件系統進行操作。
fs
模塊中提供的方法可以用來執行基本的文件操作,包括讀、寫、重命名、創建和刪除目錄以及獲取文件元數據等。每個操作文件的方法都有同步和異步兩個版本。異 步操作的版本都會使用一個回調方法作為最后一個參數。當操作完成的時候,該回調方法會被調用。而回調方法的第一個參數總是保留為操作時可能出現的異常。如 果操作正確成功,則第一個參數的值是
null
或
undefined
。而同步操作的版本的方法名稱則是在對應的異步方法之后加上一個
Sync
作為后綴。比如異步的
rename()
方法的同步版本是
renameSync()
。下面列出來了
fs
模塊中的一些常用方法,都只介紹異步操作的版本。
?
-
rename(path1, path2)
:將路徑path1
表示的目錄或文件重命名成路徑path2
。 -
truncate(fd, len)
:將文件描述符fd
對應的文件的長度截斷為len
。 -
chmod(path, mode)
:將路徑path
表示的目錄或文件的權限修改為mode
。 -
stat(path)
:獲取路徑path
表示的目錄或文件的元數據。元數據用Stats
類來表示。 -
open(path, flags, mode)
:打開一個路徑path
表示的文件?;卣{方法中可以得到該文件的描述符。 -
read(fd, buffer, offset, length, position)
:讀取給定文件描述符fd
所表示的文件中從position
位置開始的長度為length
字節的數據,并存放到緩沖區buffer
中從offset
起始的位置上?;卣{方法中可以得到實際讀取的字節數。 -
write(fd, buffer, offset, length, position)
:將緩沖區buffer
中的數據寫入到文件描述符fd
所表示的文件中。參數的含義與read()
方法一樣。回調方法中可以得到實際寫入的字節數。 -
readFile(filename, encoding)
:以編碼格式encoding
來讀取一個文件filename
中的內容。回調方法中可以得到文件的內容。 -
writeFile(filename, data, encoding)
:將數據data
以編碼格式encoding
寫入到文件filename
中。
除了上面列出來的直接操作文件本身的方法外,還可以把文件轉換成流。
?
createReadStream(path, options)
和
createWriteStream(path, options)
分別用來從文件中創建可讀和可寫流。參數
path
表示的是文件的路徑,
options
是一個表示讀取或寫入文件時的選項的 JavaScript 對象。
?
代碼清單 8 中給出了一個簡單的 HTTP 靜態文件服務器的實現。
?
清單 8. HTTP 靜態文件服務器
var http = require("http"), fs = require("fs"), path = require("path"), url = require("url"); var server = http.createServer(function(req, res) { var pathname = url.parse(req.url).pathname; var filepath = path.join("/tmp", "wwwroot", pathname); var stream = fs.createReadStream(filepath, {flags : "r", encoding : null}); stream.on("error", function() { res.writeHead(404); res.end(); }); stream.pipe(res); }); server.on("error", function(error) { console.log(error); }); server.listen(8088, "127.0.0.1");
?
如
代碼清單 8
所示,首先把 HTTP 請求的路徑轉換成服務器上文件路徑,再從文件中創建可讀流,最后通過
pipe()
方法把文件的數據流傳遞到 HTTP 請求的響應中。
?
輔助模塊
除了上面介紹的這些常見模塊之外,node.js 還提供了一些輔助的模塊。
?
模塊
path
用來處理文件系統上的路徑。這個模塊中的
join()
用來把多個路徑連接起來,形成一個完整的路徑。如
join("/usr", "home", "test/index.html")
的結果是路徑
/usr/home/test/index.html
。
normalize()
用來對路徑進行歸一化操作,去掉其中多余的“/”以及處理“..”和“.”。
?
resolve([from ...], to)
方法用來獲取給定路徑
to
的絕對路徑。如果
to
不是絕對路徑,就把它之前的參數按從右到左的順序添加上去,直到得到了一個絕對路徑。如果到最后還是無法得到絕對路徑,就把當前的工作目錄加上。假設當前的工作目錄是
/usr/home
,那么
resolve("test", "index.html")
的返回結果是
/usr/home/test/index.html
。
dirname()
方法用來獲取路徑的目錄部分。如
dirname("/usr/home/index.html")
的返回結果是
/usr/home
。
basename()
用來獲取路徑的最后一個部分。如
basename("/usr/home/index.html")
的返回結果是
index.html
。
extname()
用來獲取一個路徑的文件擴展名部分。如
extname("/usr/home/index.html")
的返回結果是
.html
。
?
模塊
url
用來對 URL 進行解析。
parse(urlStr, parseQueryString)
方法用來把一個 URL 字符串
urlStr
解析成主機名、端口和路徑等幾個部分。該方法的返回值是一個包含了
protocol
、
hostname
、
port
、
pathname
和
query
等屬性的 JavaScript 對象。如果參數
parseQueryString
的值為
true
的話,URL 中包含的查詢字符串部分也會被解析。
format(urlObj)
方法的作用與
parse()
方法正好相反,用來從一個 JavaScript 對象中構建出 URL 字符串。
?
模塊
querystring
用來處理 URL 中的查詢字符串。
stringify(obj)
方法用來把一個 JavaScript 對象
obj
轉換成查詢字符串的格式。如
stringify({a : 1, b : "good"})
的返回結果是
a=1&b=good
。
parse(str)
用來把一個查詢字符串解析成 JavaScript 對象。
?
模塊
vm
可以用來執行 JavaScript 代碼。方法
runInThisContext(code)
用來執行一段 JavaScript 代碼
code
并返回其結果。通過該方法運行的 JavaScript 代碼不能訪問當前代碼的作用域。
?
runInNewContext(code, [sandbox])
方法也是用來執行 JavaScript 代碼的,與
runInThisContext()
不同的是通過該方法運行的 JavaScript 代碼使用
sandbox
對象作為全局對象。如
runInNewContext("a + 3", {a : 4})
的返回結果是 7。
createScript(code)
方法用來預先編譯一段 JavaScript 代碼,但是并不立即執行。該方法的返回值是一個
Script
對象。該對象同樣有
runInThisContext()
和
runInNewContext([sandbox])
兩個方法,含義與上面提到的兩個方法類似。
模塊
os
提供了與底層操作系統相關的一些信息。包括
hostname()
用來獲取操作系統的主機名;
type()
用來獲取操作系統的類型;
release()
用來獲取操作系統的發行版本號;
uptime()
用來獲取以秒計算的系統運行時間;
cpus()
用來獲取 CPU 的相關信息。
freemem()
和
totalmem()
分別用來獲取系統的內存總數和可用內存數。
?
模塊
util
提供了一些常用的輔助方法。
debug(string)
方法用來輸出信息到標準錯誤流。
log(string)
方法用來輸出附帶時間戳的信息到標準輸出流。
inspect(object, showHidden, depth)
方法用來輸出一個對象的內部結構,參數
object
是要查看的對象,
showHidden
表示是否查看對象的隱藏屬性,
depth
表示查看的對象層次結構的深度,默認值是 2。
inherits(constructor, superConstructor)
方法用來實現 JavaScript 中基于原型的繼承機制。
?
在介紹完 node.js 提供的常用模塊之后,下面通過一個完整的示例來說明 node.js 的用法。
?
實例分析
這個實例實現的功能是動態監測服務器的內存使用狀態,即內存的占用率。獲取服務器上的內存占用率比較簡單,只需要使用
os
模塊提供的方法即可,即
freemem()/totalmem()
。為了能夠實時的監測內存占有率,服務器需要實時的把數據傳輸給瀏覽器端。這里最好的實現方式是 HTML 5 中引入的 WebSocket 規范。該規范在 Firefox 4 和 Google Chrome 等新瀏覽器上得到了支持。同時服務器端也需要支持此規范。
?
Socket.IO 在 node.js 上提供了對 WebSocket 規范的支持,包括服務器端和瀏覽器端代碼。 代碼清單 9 給出了使用 Socket.IO 的服務器端代碼。
?
清單 9. 監測內存占用率的服務器端代碼
var io = require('./socket.io'); var io = io.listen(server); io.on("connection", function(client){ setInterval(function() { client.send(os.freemem() / os.totalmem()); }, 500); });
?
在
代碼清單 9
中,
server
是 node.js 中的一個 HTTP 服務器對象,用來響應一般的 HTTP 請求。
?
Socket.IO 可以對 node.js 的 HTTP 服務器的請求進行攔截,將部分請求交給 Socket.IO 來處理。這里的處理邏輯是當有客戶端連接上的時候,就每隔 500 毫秒把服務器的內存占用率發送給客戶端。 代碼清單 10 給出了瀏覽器端的 HTML 和 JavaScript 代碼。
?
清單 10. 監測內存占用率的瀏覽器端代碼
<!doctype html> <html> <head> <title> 服務器內存使用情況 </title> <script src="/socket.io/socket.io.js"></script> <style> #usage {border : 1px dashed green;} </style> <script> var canvas, width = 200, height = 200, buffer = [], max = 200; function updateChart(data) { if (buffer.length >= max) { buffer.unshift(); } buffer.push(data); var ctx = canvas.getContext("2d"); ctx.clearRect(0, 0, width, height); for (var i = 1, n = buffer.length; i < n; i++) { ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(i - 1 , buffer[i - 1] * height); ctx.lineTo(i, buffer[i] * height); ctx.closePath(); ctx.stroke(); } } function onLoad() { canvas = document.getElementById("usage"); var socket = new io.Socket(null, {port: 8088}); socket.connect(); // 連接到服務器 socket.on("message", function(obj){ // 接收到消息時的處理方法 updateChart(obj); }); } </script> </head> <body onload="onLoad();"> <h1> 內存使用情況 </h1> <canvas id="usage" width="200" height="200"></canvas> </body> </html>
?
如
代碼清單 10
所示,首先建立一個與服務器之間的 WebSocket 連接。通過
message
事件定義了當接收到服務器端的消息時,更新瀏覽器端的顯示。瀏覽器端通過一個 HTML 5 提供的 canvas 來繪制內存占用率的曲線圖,如
圖 1
所示。
?
圖 1. 內存占用率的曲線圖
?
總結
一 提到服務器端開發,開發人員一般想到的就是 Java 和 C/C++ 等語言。但是通過 node.js 提供的強大能力,熟悉 JavaScript 的 Web 開發人員也可以開發出服務器端的應用。本文詳細介紹了 node.js 的事件驅動機制和模塊化結構,并對其中的常用模塊做了詳細說明,最后通過一個完整的實例展示了 node.js 的實用性。
?
?
參考資料
學習
- 查看 node.js 的官方說明文檔。
- “ The WebSocket API ”給出了 WebSocket 規范。
- 查看 Socket.IO 的說明文檔。
- developerWorks Web development 專區 :通過專門關于 Web 技術的文章和教程,擴展您在網站開發方面的技能。
- developerWorks Ajax 資源中心 :這是有關 Ajax 編程模型信息的一站式中心,包括很多文檔、教程、論壇、blog、wiki 和新聞。任何 Ajax 的新信息都能在這里找到。
- developerWorks Web 2.0 資源中心 ,這是有關 Web 2.0 相關信息的一站式中心,包括大量 Web 2.0 技術文章、教程、下載和相關技術資源。您還可以通過 Web 2.0 新手入門 欄目,迅速了解 Web 2.0 的相關概念。
- 查看 HTML5 專題 ,了解更多和 HTML5 相關的知識和動向。
獲得產品和技術
討論
- 查看 developerWorks 博客 的最新信息。
- 加入 developerWorks 中文社區 。查看開發人員推動的博客、論壇、組和維基,并與其他 developerWorks 用戶交流。
原文: http://www.ibm.com/developerworks/cn/web/1107_chengfu_nodejs/
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
