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/ にあるので、そいつを参考にするとよいと思います。