node.jsとsocket.ioでリアルタイムチャットシステムを作る 〜実装編〜

さて、前回でインストールが完了しましたので、
実際にプログラムをしていきます。

まず、前回、chatroomという名前のプロジェクトのひな形を作成したので、
それを起動してみましょう。

$ cd chatroom
$ node app.js
Express server listening on port 3000 in development mode

これで起動されましたので、
http://localhost:3000/
にアクセスすると、以下のような画面が表示されると思います。

■1、初期表示画面
こんな画面を作ります。

chatroom/app.js を以下のように編集します。

var express = require('express');
var sio = require('socket.io');

var app = module.exports = express.createServer();

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(require('stylus').middleware({ src: __dirname + '/public' }));
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
});
app.configure('production', function(){
  app.use(express.errorHandler()); 
});

app.listen(3000);
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);

// ルーティングの設定
var routes = require('./routes');
app.get('/', routes.index);

次に chatroom/views/layout.jade を以下のように編集します。

!!!
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/bootstrap/bootstrap.css')
    link(rel='stylesheet', href='/stylesheets/style.css')
    script(src='/socket.io/socket.io.js')
    script(src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js')
body!= body

次に、chatroom/views/index.jade を以下のように編集します。

div.container
	div.hero-unit
		a.titleLink(href="/")
			h1= title
		p test version
	form(action="/enter", method="post")
		div.input.center(style="margin-left:0px")
			span.label.success なまえ
			input.xlarge.username(name='username' , type="text ") 

		div.center
			input.btn.large.primary.enterBtn(type="submit", value="はいる")

次に、chatroom/public/stylesheets/style.styl を作り以下のように編集します。
なおこれはただのスタイルなので、なくてもいいです。

div.hero-unit
  height 50px

p.name
  color #00B7FD
  width 100%

div.hero-unit
	p
	  color Sienna

a.titleLink:hover
  text-decoration none

div.center
  text-align center

input.enterBtn
  margin-top 40px

div.chatArea
  text-align center
  margin-top 30px

input.chatText

input.chatBtn
  margin-left 10px

input.username
  margin-left 20px

table.chatTable
  margin auto
  width 60%
  border-bottom 1px solid #ddd

td.nameTd
  width 25%

td.chatTd
  width 75%

p.chat
  text-align left

div.loginUsers
  	margin auto auto 30px
  	width 80%
div.loginUsers span
	margin-left 15px
	background-color Khaki
	border-radius 5px 5px 5px 5px
	padding 4px 7px

div.chatDiv
  max-height 400px
  overflow auto

さて、ここまでかけたら、

$ node-dev app.js 

で起動してみましょう。きっと初期表示画面がでるはずです。きっと。

■2,チャットルームにログインする処理

まず、chatroom/app.js に以下を追記します。

var routes2 = require('./routes/enter');
app.post('/enter',routes2.enter);

次に、chatroom/routes/enter.js を作り以下のように編集します。

exports.enter = function(req, res) {
	var result = {title: 'ChatRoom',
			username: req.body.username};
	res.render('room', result);
};

次に、チャットルームの画面を定義します。
chatroom/ views/room.jade を作り以下のように編集します。

div.container
	div.hero-unit
		a.titleLink(href="/")
			h1= title
			p test version
	div.loginUsers.alert-message.block-message.warning
		p
			strong ログインユーザ :
			span #{username}
			
	div.chatDiv
		table.chatTable
			tr
				th なまえ 
				th 
			tr
	div.chatArea
		form
			input.xlarge.chatText(name="chat", type="text")
			input.btn.primary.chatBtn(type="submit", value="はつげん")

	
script(type='text/javascript')
	var user =  '#{username}';
	var sockt;
	$(function(){
		
		socket = io.connect();
		socket.emit('login', user); //ログインしたということをサーバーに送る
		socket.on('res',function(from, msg) {
			console.log('res# from=' + from + ', msg=' + msg);
			displayChat(from, msg);
		});
		socket.on('updateLoginUsers', function(users){
			console.log('updateLoginUsers# users=' + users);
			updateLoginUsers(users)
		});
		$('form').submit(function(e){
			chat();
			return false;
		});
	});
        //ログインしているユーザの更新
	function updateLoginUsers (users) {
		console.log(users);
		$('div.loginUsers p span').remove();

		for (var i in users) {
			$('div.loginUsers p').append($('<span>').text(users[i]));
		}
	}
        //発言する
	function chat() {
		var msg = $('input.chatText').val();
		$('input.chatText').val('').focus();
		socket.emit('chat', user, msg); //発言内容をサーバーに送る
		$('div.chatDiv').animate({ scrollTop: 2000000 }, 'fast');
		return false;
	}
        //発言内容を表示する
	function displayChat(from, msg) {
		console.log('chat: from=' + from + ', msg=' + msg);
		var nameDom = $('<p>').addClass('name').text(from + ' : ');
		var trDom = $('<tr>');
		trDom.append($('<td>').addClass('nameTd').append(nameDom));
		var chatDom = $('<p>').addClass('chat').text(msg);
		trDom.append($('<td>').addClass('chatTd').append(chatDom));
		$('table.chatTable').append(trDom);        		
	}

ここまで、書けたら http://localhost:3000/ にアクセスして、「はいる」ボタンを押すと、以下の画面が出ると思います。

■3,チャット処理を書く

まず、chatroom/app.js に以下を追記します。

var io = sio.listen(app);
var routes3 = require('./routes/chat');
routes3.io = io;
 // クライアントが接続してきたときの処理
io.sockets.on('connection', routes3.chat);

次に、chatroom/routes/chat.js を作り以下のように編集します。

exports.users = {};
exports.messages = [];
exports.chat = function(client) {
	var users = exports.users;
	var messages = exports.messages;
	var io = exports.io;
	 console.log(client.sessionId + 'が接続しました。');
	 
        //ログインしてきた場合の処理
	 client.on('login', function(user){
		console.log(users);
	 	users[user] = user;
		client.user = user;
		io.sockets.emit('updateLoginUsers', users);
		var msg =  client.user + 'さんがログインしました!';
		for(var key in messages) {
			client.emit('res', messages[key]['user'], messages[key]['msg']);
		}
		messages.push({'user':'system', 'msg' :msg});
		io.sockets.emit('res', 'system', msg);
	 });
	 // メッセージを受けたときの処理
	 client.on('chat', function(from, msg) {
		 console.log("メッセージを送信しました。(from=" + from + ", msg=" + msg + ")");
		 messages.push({'user':from, 'msg' :msg});
		 // つながっているクライアント全員に送信
		 io.sockets.emit('res', from, msg);
	 });

	 // クライアントが切断したときの処理
	 client.on('disconnect', function(){
		 console.log(client.sessionId + 'が切断しました。');
		 delete users[client.user];
		 io.sockets.emit('updateLoginUsers', users);
		 var msg = client.user + 'さんがログアウトしました...';
		 io.sockets.emit('res', 'system', msg);
		 messages.push({'user':'system', 'msg' :msg});
		
	 });
};


ここまで来たら以下のようにリアルタイムチャットができるようになっているはずです。

ポイントは、socket.on や socket.emit や client.on や io.sockets.emitの部分ですね。
コードが貼りつけられていて、よくわからないor動かないという方は、
https://github.com/akira-kuriyama/chatroom-websocket
にソースをまるごと上げたのでREADME.txtを読んでやってみてください。
(ただし、nodeやnpmがインストールされている前提です。)

なお、socket.ioを使用したチャット自体のサンプルは、
socket.io自体にも存在し、socket.io/examples/chat/ にあるので、そいつを参考にするとよいと思います。

VMware ServerをWindows7にインストールする

VMware Server をインストールして、
起動させてみると、
ブラウザ上に、「VMware Infrastructure Web Access」が立ち上がった。
で、ユーザ名と、パスワードを聞かれるんだけど、これはWindowsのユーザ名とパスワードを入れるらしい。
しかし、間違っていないのに、
「Login failed due to a bad username or password」と言われて、ログインできない。。

半泣きになりながら、調べてみてもどうもこれといった解決方法がみつからない。
で、以下の方法でログインできるようになりました。

Windowのアカウントの管理画面から、
管理者ユーザを新たに作り、その際パスワードも設定する。
で、まあ一応念のため再起動してそのユーザでログインもしておく。
次に、いつも使っているユーザでログインしなおし、
さきほどの「VMware Infrastructure Web Access」を立ち上げる。
ここでユーザ名とパスワードを聞かれるが、
そこには先程新規に作ったユーザ名とパスワードを入れる。
これでログイン出来るようになった。

さて、
そもそも「VMware Infrastructure Web Access」だが、IEじゃないと動かない。
今まではIEFirefoxで動作するものだったらしいが、「VMware Infrastructure Web Access」を動かすためのプラグインFirefoxのバージョンアップによって
対応されなくなったらしい。
なので、IEで「VMware Infrastructure Web Access」を開く。
そんでプラグインを入れてください的な表示が下の方にでるので、そのボタンを押してプラグインをインストールする。

そして、「VMware Infrastructure Web Access」を開いたはいいが、これではまだ正常に動かない。
恐らくhttpsで動いているはずだが、証明書がオレオレ証明書(独自証明書)なので、
[アドレスバー付近の証明書のエラーをクリック]→[証明書の表示]→[証明書のインストール]をおして、
で証明書のインポート画面に移るので、[次へ]を連打して[完了]でOK.
これで、きっと「VMware Infrastructure Web Access」が正常に動くようになるはずだ。

しっかし、そもそもブラウザで動作するとかIEでしか動作しないとか、
画面UIが分かりづらいとか、VMwareは一体どうなっているんだ…。

VMwareのネットワーク設定をする

さて、前回VMwareのインストールをしたので、ネットワーク設定をしていく。

VMwareのネットワーク設定はいつも忘れてしまうので、ちゃんと書き残していこう。


まず接続の種類。
NAT接続と、ブリッジ接続と、ホストオンリー接続がある。
それぞれの特徴は以下のサイトを見てくださいね(丸投げ)
VMwareの仮想ネットワークの種類


今回はNAT接続をする。
これは以下のサイトが詳しい。
Windows7 で VMwareの NAT 接続ができないとき

Windowsの設定

Windowsのネットワークの設定を開くと、
VMware Network Adapter VMnet1」と
VMware Network Adapter VMnet8」が作成されているはず。
ここでポイントは
VMnet1はホストオンリー接続用の設定で、
VMnet8はNAT接続用の設定になる。
いったんVMnet8の設定はおいておく。

Windowsのネットワークの設定を開くと、以下のように自分がいつも使っているネットワーク設定があるはずだ。
以下の画像でいうと、青い丸で囲ったocnの部分。


この"ocn"のリンクをクリックする。
そしたら[プロパティ]クリック→[共有]タブをクリック→[ネットワークをほかのユーザに、このコンピューターのインターネット接続をとおしての接続を許可する]にチェックを入れる
そしてその下の[ホーム ネットワーク接続]のセレクトボックスは、「VMware Network Adapter VMnet8」を選択する。

これで、VMwareからのインターネット接続を受け付けることが可能になった。つまりVMwareのインターネット接続を肩代わりしている形だ。

VMwareの設定

VMware Serverをインストールしたフォルダの下に
vmnetcfg.exeというプログラムがあるはずだ。こいつを管理者としてこのプログラムを実行する
要は右クリックして互換性タブクリックして下の方のチェックボックスにチェックいれてOK押す。
(スタートメニューから[VMware]→[VMware Server]→[Manage Virtual Networks]から管理者として実行でもいい)
すると、以下のような「Virtual Network Editor」が立ち上がる。

VMware Playerの場合は、vmnetcfg.exeが同梱されていないようなので、以下のサイトに従って入手する。
http://gendosu.jp/?p=57


そしたら、[Host Virtual Network Mapping]タプをクリックし、
[VMnet8]の「>」ボタンをクリックし、[Subnet...]を選択。
[IP Adress]を[192.168.137.0]に、[Subnet Mask]を[255.255.255.0]に設定。
さらにまた[VMnet8]の「>」ボタンをクリックし、[NAT...]を選択。
Gateway IP address」を「192.168.137.1」にし、DNSのIP Addressも「192.168.137.1」にする。
そしてOKボタンを押して、「Virtual Network Editor」の設定を完了する。

■Virtual Machineの設定

さて、最後は、Virtual Machineのネットワーク設定をNATにする。
VMware Infrastructure Web Access」画面からVirtual Machineをcreateすると、以下のような画面になるはず。

で、青い丸で囲ったところから、[Network Connection]を[NAT]にする。

                                                                        • -

これで、きっとネットワーク接続ができるようになっているはず。
しかしこれみんなハマっているんじゃないだろうか…。