Node.jsをいまさら導入、Hello, Worldをじっくり理解してみた

2012/5/22 node.js

いまさらながらnode.jsを試してみました。 まずは最初の例のプログラムを動かし、徹底的に関数一つ一つじっくりと読んでみました。

まずはnode.jsのインストール

mac portsのDBを最新にアップデートします

sudo port selfupdate

Password:(パスワード入力) —> Updating MacPorts base sources using rsync MacPorts base version 2.0.4 installed, MacPorts base version 2.1.1 downloaded. —> Updating the ports tree —> MacPorts base is outdated, installing new version 2.1.1 Installing new MacPorts release in /opt/local as root:admin; permissions 0755; Tcl-Package in /Library/Tcl The ports tree has been updated. To upgrade your installed ports, you should run port upgrade outdated

macportsからnode.jsをインストール!

sudo port install nodejs

—> Fetching archive for expat —> Attempting to fetch expat-2.1.0_0.darwin_10.x86_64.tbz2 from http://packages.macports.org/expat —> Attempting to fetch expat-2.1.0_0.darwin_10.x86_64.tbz2.rmd160 from http://packages.macports.org/expat —> Installing expat @2.1.0_0 —> Cleaning expat —> Deactivating expat @2.0.1_1 —> Cleaning expat —> Activating expat @2.1.0_0 —> Cleaning expat —> Fetching archive for zlib —> Attempting to fetch zlib-1.2.7_0.darwin_10.x86_64.tbz2 from http://packages.macports.org/zlib —> Attempting to fetch zlib-1.2.7_0.darwin_10.x86_64.tbz2.rmd160 from http://packages.macports.org/zlib —> Installing zlib @1.2.7_0 —> Cleaning zlib —> Deactivating zlib @1.2.6_0 —> Cleaning zlib —> Activating zlib @1.2.7_0 —> Cleaning zlib —> Computing dependencies for openssl —> Fetching archive for openssl —> Attempting to fetch openssl-1.0.1c_0.darwin_10.x86_64.tbz2 from http://packages.macports.org/openssl —> Attempting to fetch openssl-1.0.1c_0.darwin_10.x86_64.tbz2.rmd160 from http://packages.macports.org/openssl —> Installing openssl @1.0.1c_0 —> Cleaning openssl —> Computing dependencies for openssl —> Deactivating openssl @1.0.0g_0 —> Cleaning openssl —> Activating openssl @1.0.1c_0 —> Cleaning openssl —> Computing dependencies for sqlite3 —> Fetching archive for sqlite3 —> Attempting to fetch sqlite3-3.7.12_0.darwin_10.x86_64.tbz2 from http://packages.macports.org/sqlite3 —> Attempting to fetch sqlite3-3.7.12_0.darwin_10.x86_64.tbz2.rmd160 from http://packages.macports.org/sqlite3 —> Installing sqlite3 @3.7.12_0 —> Cleaning sqlite3 —> Computing dependencies for sqlite3 —> Deactivating sqlite3 @3.7.10_0 —> Cleaning sqlite3 —> Activating sqlite3 @3.7.12_0 —> Cleaning sqlite3 —> Computing dependencies for python27 —> Fetching archive for python27 —> Attempting to fetch python27-2.7.3_0.darwin_10.x86_64.tbz2 from http://packages.macports.org/python27 —> Attempting to fetch python27-2.7.3_0.darwin_10.x86_64.tbz2.rmd160 from http://packages.macports.org/python27 —> Installing python27 @2.7.3_0 —> Cleaning python27 —> Computing dependencies for python27 —> Deactivating python27 @2.7.2_4 —> Cleaning python27 —> Activating python27 @2.7.3_0 To make python 2.7 the default (i.e. the version you get when you run ‘python’), please run: sudo port select –set python python27 —> Cleaning python27 —> Computing dependencies for nodejs —> Fetching archive for nodejs —> Attempting to fetch nodejs-0.6.18_0+python27+ssl.darwin_10.x86_64.tbz2 from http://packages.macports.org/nodejs —> Attempting to fetch nodejs-0.6.18_0+python27+ssl.darwin_10.x86_64.tbz2.rmd160 from http://packages.macports.org/nodejs —> Installing nodejs @0.6.18_0+python27+ssl —> Activating nodejs @0.6.18_0+python27+ssl —> Cleaning nodejs —> Updating database of binaries: 100.0% —> Scanning binaries for linking errors: 100.0% —> No broken files found.

これでnode.jsはインストールできました。 確認してみます。

node -v

v0.6.18

インストールできてますね

はじめてのnode.js

はじめてのプログラムといえば「Hello, world!」ですよね。 node.jsでやってみましよう。 好きなところに「hello.js」で下のスクリプトを書いてみます。 このソースは Node.jsのトップページ に書いてあるやつを少しカスタマイズした奴です。

 var http = require('http'); http.createServer(function (req, res){ res.writeHead(200, {'Content-Type' : 'text/plain'}); res.end('Hello, World!\\n'); }).listen(3000,"127.0.0.1"); console.log('Server running at http://127.0.0.1:3000/'); 

ターミナルから起動します

node hello.js

Server running at http://127.0.0.1:3000/

ブラウザからhttp://127.0.0.1:3000/にアクセスしてみます。

Hello, World!

きちんと表示されたかと思います。 ちなみに、止めるときは強制的にターミナルでcontrol + cで止めました。

ソース解説

自分の理解度も含めるのを兼ねて、ちょっとソースを解説。 主に参照したドキュメントはhttpライブラリのドキュメントです。 まず

var http = require('http');

これは、ライブラリの読み込みですね。 cで言うところの#include、javaでいうところのimportでしょうか。 ちょっと特徴的なのが、返り値があるというところで、この返り値をオブジェクトとして使うとこでそのライブラリの機能を使うようです。 言い換えれば、外部に書いてあるクラスを読み込んで返す、ということですね。 ライブラリをロードすると同時に強制的にかつ自由に名前空間をつけるという少し不思議ですこしかしこい方法のようです。 で、これで読み込んだhttpライブラリを使ってサーバーを作ります。

 http.createServer(function (req, res){ res.writeHead(200, {'Content-Type' : 'text/plain'}); res.end('Hello, World!\\n'); }).listen(3000,"127.0.0.1"); 

まず、http.createServerを呼び出しています。 この関数、ドキュメントによれば

http.createServer([requestListener])# Returns a new web server object. The requestListener is a function which is automatically added to the ‘request’ event.

となっていて、新しくサーバーを作ると同時に、引数をListenerとしてリクエストが来た時に実行するものとして登録するようです。 もちろんイベントリスナーへの登録とサーバーの生成は別々にもできて、

 var server = http.createServer(); server.on('request',function (req, res){ res.writeHead(200, {'Content-Type' : 'text/plain'}); res.end('Hello, World!\\n'); }); server.listen(3000,"127.0.0.1"); 

ともかけるようです。 (on関数がイベントリスナーへの登録らしい) で、リクエストが来た時のイベントリスナーから呼ばれる関数ですが、これは形が決まっていて2つの引数を取るようです。

Event: ‘request’ function (request, response) { } Emitted each time there is a request. Note that there may be multiple requests per connection (in the case of keep-alive connections). request is an instance of http.ServerRequest and response is an instance of http.ServerResponse

requestとresponse、そのままですね。 requestがユーザー情報とかパラメーターとかが入っていて、responseが返すレスポンスを管理します。 今回は出力しかしないのでresponseのみ使います。 responseで使ったのはwriteHeadとendメソッドですね。 まずwriteHeadから。

response.writeHead(statusCode, [reasonPhrase], [headers]) Sends a response header to the request. The status code is a 3-digit HTTP status code, like 404. The last argument, headers, are the response headers. Optionally one can give a human-readable reasonPhrase as the second argument. This method must only be called once on a message and it must be called before response.end() is called. If you call response.write() or response.end() before calling this, the implicit/mutable headers will be calculated and call this function for you. Note: that Content-Length is given in bytes not characters. The above example works because the string ‘hello world’ contains only single byte characters. If the body contains higher coded characters then Buffer.byteLength() should be used to determine the number of bytes in a given encoding. And Node does not check whether Content-Length and the length of the body which has been transmitted are equal or not.

writeHeadはそれぞれレスポンスのヘッダを設定するメソッド、出力を終了するメソッドのようです。 ドキュメントには一度しかよぶなって書いてあるので、そこは注意しましょう。 ちなみに、これも省略形らしく、

 res.statusCode = 200; res.setHeader('Content-Type', 'text/plain');

と分けて書くこともできます。 次にendのですが

response.end([data], [encoding]) This method signals to the server that all of the response headers and body has been sent; that server should consider this message complete. The method, response.end(), MUST be called on each response. If data is specified, it is equivalent to calling response.write(data, encoding) followed by response.end().

レスポンスの終了をするメソッドです。 これを書かないと、レスポンスが切れなくてブラウザがずっとロード状態になりました。 引数の’Hello, World!\n’の方はサブで、ここに書かれてると一緒に出力してくれるようです。 別々に書くと

 res.write('Hello, World!\\n'); res.end(); 

となります。 さて、これでリクエストが来た時のレスポンスについては終わりました。 後2行をさらっと見てみます。

 (省略).listen(3000,"127.0.0.1"); 

の部分ですが、createServerで作られたserverのメソッドです。 listenというのはそのまま聞くということで、サーバーの要求待ち状態になることです。 listenメソッドの形式は2つあるようですが、今回はこちらの形式を使ってます。

server.listen(port, [hostname], [callback])# Begin accepting connections on the specified port and hostname. If the hostname is omitted, the server will accept connections directed to any IPv4 address (INADDR_ANY). To listen to a unix socket, supply a filename instead of port and hostname. This function is asynchronous. The last parameter callback will be added as a listener for the ‘listening’ event. See also net.Server.listen().

ポート番号、ホスト名、コールバックを引数に取る形式です。 それぞれ何番ポートで動作するのか、どのPCからのアクセスを許容するのか、listenしたときのイベントリスナーへ登録する関数ですね。 ポート番号は他のプログラムとかぶっていなければOKなのでとりあえず3000にしてみました。 ホスト名はローカルでしか試さないので127.0.0.1を、 イベントへの登録は今回はなしですね。 この関数をよんだ後ならばブラウザからアクセスできる、というわけです。 最後、console.logですね これは普通のjsでもあったと思うのですが、ただの出力です。 ここに書くことで、ターミナルにメッセージが書かれる、ということです。

少し長くなりましたが、初期プログラムをじっくりと解説してみました。 node.jsは数行でサーバーが立てられてしまうのが多白いですね。 phpなどapache上で動くスクリプトばっかやってきた自分としては新鮮です。 公式ドキュメントも結構読みやすく、みんなが騒いでたのもわかるなぁといまさらながらにおもってます。

img_show