node.js、socket.ioでチャット。切断中に受け取れなかったデータを再接続時に取得
概要
websocketの入門者の定番の「チャット」をnode.js+socket.io(とexpressを少々)で実装。
クライアントの接続が途中で切断された場合、再接続時にメッセージを取得する処理も実装したメモ。
(nod32のHTTPチェックが、websocketを定期的に切断したので・・)
・全ソース
github: https://github.com/motsat/node-chat/
←node server.js
でサーバ起動し、
ブラウザで
http://起動したサーバIP:8000
すれば動く!はず。
送受信するメッセージオブジェクト
このメッセージオブジェクトでやりとりする。
サーバから、クライアントからのどちらでも基本的に形は同じ。
{ "data" : "こんにちは", "type" : 1, // 通常の文字メッセージ=1, 取得要求=0 "option" : {"color" : "red"},// メッセージの場合の色など。他は今は未使用 "createdAt" : "2010-11-11 11:11:11"} ;
処理の流れ
最初の接続時、サーバへのソケット接続をトリガーとしてチャットメッセージを取得。
サーバへのデータ取得時、more_id=1で1番目のメッセージから取得。
接続中にチャットを送信した場合、サーバからbroadcastして他のクライアントへ送信。
また、送信元のクライアントへも同じくチャットを送信。
一時的にソケットが切断され、再接続した場合は損失分のメッセージ取得。
n個までのメッセージを取得済みだった場合、
サーバへのデータ取得時、more_id=n+1とし、n+1番目のメッセージから取得。
ソース
[クライアント側(index.ejs)]
<html> <head> <script src="http://motoki.local:8000/js/socket.io.js"></script> <script src="http://motoki.local:8000/js/json2.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script> <script> //{{{ messageBoad Message = function(id){ var data = []; return { add : function (msg) {data.push(msg)}, length : function (msg) {return data.length}} }; //{{{ messageBoad MessageBoad = function(id){ var datas = [], _id = id, formatData = function (data) {return "["+data.createdAt + "] : " + data.data}; return { add : function (msg) {datas.push(msg); $('<div></div>').text(formatData(msg)).css('color', msg.option.color).appendTo(_id); }, length : function () {return datas.length;} } }; //}}} //{{{displayStatus DisplayStatus = function(id){ var _id = id; return { connect : function () {$(_id).text('connecting..').css('color','blue')}, disconnect : function () {$(_id).text('disconnected').css('color','red')}, setId : function (id) {_id = id}} }; //}}} var socket = new io.Socket('motoki.local',{port:8000}), dispStatus = new DisplayStatus('div.#connect_status'), msgBoad = new MessageBoad('div.#message_boad'), messageType = {'GET_DATA' : 0, 'STRING' : 1}; socket.connect(); socket.on('connect', onConnect); socket.on('disconnect', onDisconnect); socket.on('message', onMessage); $(function(){ $('input#send_button').bind('click', sendMessage);; $('input#switch_socket_button').bind('click', switchSocketConnect);; }); //{{{sendMessage function sendMessage() { var msg = {"type" : messageType.STRING, "option" : {"color" : ""}, "data" : $('input#send_body').val()}; try{ socket.send(msg); } catch(e) { alert('socket.send() error : ' + e); } } //}}} function onConnect() { log('onConnect'); var msg = {"type" : messageType.GET_DATA, "more_id" : msgBoad.length()}; socket.send(msg); dispStatus.connect(); } function onDisconnect() { log('onDisconnect'); dispStatus.disconnect(); } function onMessage(msg) { msgBoad.add(msg); } function switchSocketConnect(msg) { if (socket.connected) { socket.disconnect(); } else { socket.connect(); } } //{{{log function log(msg) { if (typeof console == 'object') { console.log(msg); } } //}}} </script> </head> <body> <div id='connect_status'>choge</div> message : <input id='send_body' value='' type="text"/> <input type="button" id='send_button' value='送信'/> <div><input type="button" id='switch_socket_button' value='ソケットON/Off'/></div> <hr/> <div id='message_boad'></div> <hr/> </body> </html>
サーバー側(server.js)
var io = require('socket.io'), express = require('express'), messages = [], messageType = {'GET_DATA' : 0, 'STRING' : 1} ; // GET=取得要求、 STRING=チャットメッセージ var app = express.createServer(); app.configure(function(){ app.set('views', __dirname + '/views'); app.use(express.static(__dirname + '/public')); app.set("view options", { layout: false }); app.get('/', function(req, res) { res.render('index.ejs'); }); }); app.listen(8000); var socket = io.listen(app); socket.on('connection', function(client){ client.on('message', function(msg) {onMessage(msg, client);}); client.on('disconnect', function() {onDisconnect(client)}); }); function addMessage(msg) { msg.id = messages.length + 1; msg.createdAt = getNowDate(); if (msg.option.color == '') { msg.option.color = 'black'; } messages.push(msg); return msg; } function onDisconnect(client) { msg = {"data" : client. sessionId+"が切断されました", "type" : messageType.STGING, "option" : {"color" : "red"}, "createdAt" : getNowDate()} ; socket.broadcast(msg); } function onMessage(msg, client) { if (msg.type == messageType.GET_DATA) { if (typeof msg.more_id == 'number') { client.send(messages.slice(msg.more_id)); } } else { addMessage(msg); client.send(msg); client.broadcast(msg); } } function getNowDate(){ d = new Date(); return d.getFullYear() + "-" + (d.getMonth()+1) + "-" + d.getDate()+ " "+ d.getHours()+':' +d.getMinutes()+':' + d.getSeconds(); } //}}}