HTML5でサーバプッシュ(Node.jsとWebSocket)

HTML5 Advent Calendar2011の6日目。

私がHTML5の中で最も注目しているWebSocketについて書きます。
今更説明するまでもないのですが、WebSocketはHTML5で実装されたサーバプッシュを
「簡単に」実現させることができます。

サーバプッシュと言うくらいなので、当然サーバ側の仕組みが必要で、
WebSocketサーバを実現するライブラリが世の中には既にかなりありますが、
なんといってもNode.jsを使ったNode WebSocket Serverがお手軽で、
クライアントサイドと同じJavaScriptで書けるのが素敵。


とまぁ、能書きはいいので、簡単なサンプルを。
Node.jsのインストールはこちら辺りを参考にどぞ。
Node WebSocket Serevrはnpmコマンドでインストールします。

#> npm install websocket-server

これでカレントディレクトリのnode_modules/以下にインストールされる。

ではまずクライアント側から。

index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=yes" />
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<title>ニュース速報</title>
<script type="text/javascript">
window.onload = function() {
	//WebSocketオープン
	socket = new WebSocket("ws://192.168.12.50:10080");
	socket.onmessage = function(event) {
		document.getElementById('news').innerHTML = event.data;
	}
}
</script>
</head>
<body>
ニュース受信中:<div id="news"></div>
</body>
</html>

IPアドレスは適当に自分の環境に合わせて書き換えてください。
またポート番号は10080にしていますが、これも自分の環境に合わせて。
当然WebSocketサーバ側でListenするポート番号と一致している必要があります。
このサンプルはサーバプッシュされてきた文字列を単に表示させているだけ。

次にサーバ側。

先ほどのnode_modules/ というディレクトリと同階層にこのjsファイルを設置します。
newsServer.js

var sys = require("util");
var fs = require("fs");
var ws = require("websocket-server");
var server = ws.createServer();

server.addListener("connection", function(connection){
	var client_id = connection.id;
	sys.log("Connected->"+client_id);
	readFile();

	fs.watchFile('./news.dat',readFile);
});

function readFile(curr,prev) {
	fs.readFile('./news.dat','UTF-8', function(err,data) {
		sys.log("news->"+data);
		server.broadcast(data);	
	});
}

server.listen(10080);

このjsファイルと同じ階層にnews.datというファイルを作成し、
このファイル内にテキストで適当にニュースなりメッセージなり書いておきます。

#> echo "This is first news." > news.dat

準備ができたらWebSocketサーバを起動しておきます。コマンドラインから、

#> node newsServer.js
node newsServer.js

で起動。


ブラウザでindex.htmlを開きます。
WebSocketに対応するブラウザはwikipedia:WebSocketを参照。
ただMacChromeだとうまく動かない。Win版Chromeは不明。
Opera11なら(設定すれば)動作可。SafariはWinでもMacでもOK。
iPhone/iPadでも可。Androidは不可。

ブラウザで開くと、サーバ側のコンソールには、

5 Dec 23:44:47 - Connected->3942536650
5 Dec 23:44:57 - news->This is first news.

なんて出ます。This is first news.ってのがnews.datの中身。
クライアント側のブラウザには

ニュース受信中:
This is first news.

となってるはず。

で、news.datを書き換える。

echo "second news" > news.dat

するとこの内容がクライアント側にほぼ瞬時に伝わることが分かるでしょう。

まぁこれはあくまでもサンプルなので何の実用性もないけども、例えばnews.datではなく
ニュースの内容をDBに登録しておき、最新ニュースがDBに登録されたら
クライアントにサーバプッシュしたり、野球やサッカーの試合速報、チャット、
対戦型ゲーム、オークションサイトでの応札状況、株価、ショッピングサイトでのタイムセール、etc・・・
将来的には緊急地震速報をサーバプッシュで配信するのもありかも(※今は安定性や対応ブラウザが少ない)。

注:ここから宣伝。

今月末に、小生が執筆したHTML5を使ったスマートフォン向けWebアプリ制作の本が
秀和システムから発刊されることになりました。
その中でこのNode.js+Web Socket Serverを使ったサンプルアプリを紹介していますので、
興味のある方はよろしければどうぞ。

注:ここまで宣伝乙。


と、まぁ応用範囲はアイディア次第で無限なWebSocketに、今後も期待大、というお話でした。

上梓しました

今更な感じもするが、9月末にPHP本を上梓しました。
一人で書いたのでなく4人の共同執筆ですが一応。

PHP逆引き大全516の極意

PHP逆引き大全516の極意


現在2冊目の校正の真っ最中。


死にそう(苦笑)

VMware Fusion上のXWindowで勝手にCAPS LOCKになってしまう件

意外とネット上で解決策が見つからなかったので自分用メモ。
自分はCentOSだが恐らく他のLinuxディストリビューションでも同様だと思われる。(XWindow環境での話だから)
※少なくとも手元のFedoraでも同様だった

原因はMacの「英数」キーがCAPS LOCKに割り振られてしまうため。
決して勝手にCAPS LOCKされてしまうのではなく、英数キーがCAPS LOCKに割り振られているためで、
入力中に無意識に英数キーを押しているのでCAPS LOCKになってしまう。
要はFusionのキーマップが悪いのだけど、プラスMacで日本語キーボードを使ってるから発生する問題。
US配列キーボード使っていればこれは発生しない(はず)。


ってことで、解決方法としてはXWindowのキーマップを書き換えてしまう。
英数キーのKeycodeは66で、それがCaps Lockにバインドされているために起こる。
つまりKeycode66を他のキーに割り当ててしまえばいい。
自分の場合は無変換に割り当ててしまった。


vi ~/.XModmap

keycode 66 = Muhenkan
clear Lock


これで一端ログアウトしてからログイン。
英数キーを押してもCAPS LOCKされないことを確認。


ちなみに同じ方法でかなキーで日本語入力Onにすることもできる(はず)。
かなキーのkeycodeは208。
自分の場合はLinux上で日本語入力することが無いのでSCIMすらいれてないので
試しようがないが・・・。

Parallels仮想環境上のCentOS Apache で静的ファイルがレスポンスされない

あまりこの問題でブチ当たる人はいないと思うけど・・・


MacOSX上のParallels DesktopLinuxCentOS)をゲストOSとして起動、
開発環境でApacheを使っている場合の話。


静的なファイル(JPGとかJavaScriptとか)へのリクエストが帰って来ず、ブラウザが真っ白になる。
(レスポンスデータが0byte)。同環境でVMware Fusionだと問題ない。
ApacheのDocumentRootは、ParallelsMac上のディレクトリを共有。


結論からいうと、このホストOS上のディレクトリをゲストOSが共有して、ApahceのDocumentRootとしている場合。
Parallelsの場合はParallels ToolというのをゲストOS作成時にインストールするのだが、
これがLinuxカーネルの一部を書き換えてる模様(Toolインストール時にkernel sourceのパッケージを要求される)。
これが悪さして、Apacheがクライアントに素早くレスポンスするためにOSに対して発行するSendfile syscallが
動作しないらしい。


解決策はここ。
http://httpd.apache.org/docs/2.0/faq/error.html
Sendfileをディレクティブでoffにしろ、と。


ディレクティブで
EnableSendFile Off
としてApache再起動。


これで動作しました。改めてググると、VirtualBoxでも同様らしい。
今までVMware Fusion3使ってて、Fusion4の出来の悪さに嫌気がさしてPara7に移行したんだけど・・・・
環境の移行って簡単にやるとこういうことでハマるというお話。

Xcode4

そろそろ1年前から構想を練ってるiPhoneアプリでもつくるか・・・・
と思ったら、Xcodeが4になってた。

そーいえば、ちょっと前にメールきてたな、と思いつつ
4GBものファイルを落としてインストール。

ちょっ・・・これ・・・・。

InterfaceBuilderがXcodeに統合され、最近のAppleの流行りのUIになってた。
24インチiMacならいいけど、これじゃノートで開発するのはまず無理だろ・・・。

で、何がどこにあるのかさっぱり分からん。
3.xに戻そうか。いや、でもこれからはこっちに慣れないと・・と格闘すること2時間。
・・・・ウインドのタイトルバーを消すプロパティがどうしても見つからん。
そんなことに2時間もかかったことに呆然としながら、今日はもう寝よう。

Xcode4の使い方ガイドってどっかにないかなー。

iOS4.3 パーソナルホットスポット解禁

http://www.gizmodo.jp/2011/03/ios_43311.html

まぁ今までもSIMフリーiPhoneならBluetooth接続で、デザリングできていたけど、
今後は無線LAN経由でデザリングできる、というのは素直に嬉しい。
SB版iPhoneユーザを横目に優越感に浸れる(笑)


ただ・・・auのhtc EVO Wimaxも正直気になる。
http://www.au.kddi.com/seihin/ichiran/smartphone/isw11ht/index.html
無線LAN経由のデザリングに加え、Wimaxがついてるのは惹かれるなぁ。
でもAndroidはなぁ・・・。


しかし巷ではAndroidが絶好調らしいが、果たしてこれいつまで続くかね。
一時期のブームで終わらなきゃいいけど。


UIの洗練度から言うと、そこはやっぱりAppleGoogle、金の使い方が違う。
Androidを触ってるとどうも直感的でないというか。
Macを普段使っててたまにWinを使うと、Macで直感的にやっていた操作がWinで
できなくて、そこで手が止まる、そんな感覚とAndroidはよく似てる。


まぁもちろん優れてるほうが普及する、とは限らないのが世の常。
Winと同じようにAndroidも勢力を伸ばしていくのかな。


http://www.itmedia.co.jp/news/articles/1101/24/news070.html
auiPhoneを虎視眈々と狙ってるらしいので、もし本当にauからiPhone出たら
またちょっと変わるかもしれない。もちろんデザリング機能をOffなんて、
ケチ臭いどっかの禿みたいなことはしないでね。
もう一つ。お願いだからiPhoneLISMO.appなんて載せなくていいですからね。

Postfixでネットワークアドレスで拒否

最近フィリピンのIPアドレスからの迷惑メールがやたらと多い。
平日に10通、週末になると1日で30通近くくる。

セフレ仕分けSNSだの、丸見えアド掲示板だの、40型液晶テレビが800円だの。
ヘッダーを見ると全てフィリピンの数個のIPアドレスから。

メールサーバはPostfixS25R+tarpitting+greylistingしてるが、
これすらスリ抜けてくる。
まぁクライアント側のメールソフトで全部迷惑メールフォルダに直行なので、最初は放っておいたんだけど、
段々腹がたってきたので、どうせフィリピンから仕事のメールが来ることは絶対にないので
フィリピン国内のIPアドレスを全て拒否することにした。

Postfixなので、単純にmain.cfに設定を書けばいい、と思ってたけどここで思いのほかハマる。

main.cfに以下を追加。

smtpd_client_restrictions =
permit_mynetworks
check_client_access cidr:/etc/postfix/reject_ip

最初、cidrという記述方法が分かっていなかった。
reject_ipは以下のように記述

180.xxx.0.0/15  REJECT
180.xxy.0.0/14  REJECT
180.xxz.0.0/16  REJECT

hashでIPアドレス一個づつ書いてると鬼のようなリストになるので、ネットワークアドレスで
拒否できるのは有り難い。
実際の各国別IPのリストはakionweb様より拝借。

で、このリストをpostmapするのか、と思っていたらどうやらしなくてもいいらしい。
このままでpostfixをreload。

で、テストしようと試しに自分のISPのネットワークアドレスを上記のreject_ipの拒否リストに入れて、
telnet xxx.xxx.xxx.xxx 25 する。

Escape character is '^]'.
220 mail.hogehoge.net ESMTP Postfix
EHLO localhost
250-mail.hogehoge.net
250-PIPELINING
250-SIZE 1048576
250-ETRN
250-AUTH PLAIN LOGIN
250-AUTH=PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
MAIL FROM: hoge@hogehoge.com
250 2.1.0 Ok

となり接続できてしまう。

あれれ???と何回も設定ファイルやネットワークアドレスを確認するも、同じ。
なんでだろー??と思って1時間悩む。が、特に間違えてないっぽい。

まさか・・・と思い、RCPT TOまで入れてみる

RCPT TO: hogeuser@hogehoge.com
554 5.7.1 <111x222x333x444.ap.hoge.ne.jp[111.222.333.444]>: Client host rejected: Access denied

orz

qmailtcpserverようにSMTP接続時点で拒否るのではなく、RCPT TO までいれた後で判定されるらしい。
RCPT TOコマンドまで発行されるのはいただけないが、まぁちゃんと拒否ってくれるのでよしとする。

なんと無駄な時間・・・・。