というわけで、最近勉強してたSocket.IOでサンプルを作りました。
このソースをまるごと持って行ってIPなりポートなり調整すれば動くはずです。
おかげでロジック自体はシンプルになったけど、使い勝手云々いじると必然的にクライアント側のコードが多く・・。
今回は久しぶりにjQueryも使ってます。
仕様
- 部屋とニックネームを決めて入室し、その部屋でリアルタイムチャット
- 違う部屋の内容は見れない
- エンターキーでPOSTできる
- 一回入った部屋を出て、違う部屋に入れる
- スマートフォンでもキレイに見れる
- システムメッセージとユーザーのメッセージは住み分ける
などなど。
ソース
Html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta name="format-detection" content="telephone=no"> <title>Hello Socket.IO</title> <style> /* 後述 */ </style> </head> <body> <div id="container"> <header> <h1>Socket IO chat</h1> <button id="leave">Leave</button> </header> <div id="page1"> <p> This is sample of socket.IO chat.<br> Select [Room] and type your [Name].<br> And press [Join] to start! </p> <div class="init"> <label> <span class="title">Room:</span> <select id="room"> <option value="roomA">#A</option> <option value="roomB">#B</option> <option value="roomC">#C</option> </select> </label> <label> <span class="title">Name:</span> <input type="text" value="John" id="name" maxlength="10"/> </label> </div> <button id="join">Join</button> </div> <div id="page2"> <ul id="view" class="list"></ul> <div class="msgBox"> <input type="text" placeholder="Please type messages here." id="message" /> </div> </div> </div> <script src="/socket.io/socket.io.js"></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script> <script> // 後述 </script> </body> </html>
Css
body { margin:0; padding:0; } p { margin:0; } h1 { position:fixed; top:0; margin:0; padding:8px 0; width:100%; height:32px; background:#000; color:#fff; text-align:center; font-size:1.2rem; line-height:32px; } #leave { position:fixed; top:8px; right:8px; padding:3px 5px; height:32px; border-radius:8px; background:#165e83; color:#fff; } #page1,#page2 { margin-top:48px; } #page1 { padding:8px; } .init { margin:16px auto; padding:8px; border:1px solid #ccc; background:#eee; } .init > label { display:block; margin-bottom:8px; height:44px; font-size:1.4rem; line-height:44px; } .init > label > .title { display:inline-block; width:30%; color:#aaa; } .init > label > input[type="text"] { display:inline-block; width:61%; height:38px; font-size:1.2rem; } #page1 > button { display:block; box-sizing:border-box; margin:0 auto; width:100%; height:44px; border-radius:4px; background:#0094c8; color:#fff; font-size:1.5rem; -webkit-appearance:none; } #page2 .msgBox { margin:8px auto; padding:0 8px; text-align:center; } .msgBox input[type="text"] { -webkit-box-sizing:border-box; width:100%; height:38px; font-size:1.2rem; } .list { margin:0 auto; padding:0; border-top:1px solid #ccc; border-bottom:1px solid #fff; list-style:none; } .list li { padding:8px; border-top:1px #fff solid; border-bottom:1px #ccc solid; font-size:1.1rem; } .list li:nth-child(even) { background:#eee; }
Client side JavaScript
$(function(io, $) { var socket = io.connect('http://localhost/chat'), $html = $('html,body'), page1 = $('#page1'), page2 = $('#page2').hide(), leaveBtn = $('#leave').hide(), roomBox = $('#room'), nameBox = $('#name'), joinBtn = $('#join'), msgBox = $('#message'), msgList = $('#view'); socket.on('connected', function() { console.log('[System]Welcome to simple chat!'); }); joinBtn.on('click', function() { var r = roomBox.val(); var n = nameBox.val(); chat(r, n); }); function chat(room, name) { socket.json.emit('init', { 'room': room, 'name': name }); } socket.on('initialized', function() { page1.hide(); page2.show(); leaveBtn.show(); }); leaveBtn.on('click', function() { socket.emit('leave'); page2.hide(); leaveBtn.hide(); page1.show(); msgList.empty(); }); function send() { var data = msgBox.val(); socket.json.send(data); msgBox.val(''); } msgBox.focus(function() { page2.bind('keypress', sendByTypeEnter); }); msgBox.blur(function() { page2.unbind('keypress', sendByTypeEnter); }); var sendByTypeEnter = function(e) { if (e.keyCode === 13) { send(); } }; function update(data, systemFlag) { var list = $('<li>').html(data); if (systemFlag) { list.css('color', '#aaa'); } msgList.append(list); $html.animate({ scrollTop: list.offset().top }, 'fast'); } socket.on('message', function(data) { if (data) { update(data); } }); socket.on('System', function(data) { var systemFlag = true; if (data) { update(data, systemFlag); } }); }(io, jQuery));
Server side JavaScript
var app = require('http').createServer(handler), io = require('socket.io').listen(app), fs = require('fs') app.listen(8080); function handler(req, res) { fs.readFile(__dirname + '/index.html', function(err, data) { if (err) { res.writeHead(500); return res.end('Error loading index.html'); } res.writeHead(200); res.end(data); }); } function getRoomAndNameObj(client, obj) { client.get('room', function(err, $room) { obj.room = $room; }); client.get('name', function(err, $name) { obj.name = $name; }); return obj; } var socket = io.of('/chat').on('connection', function(client) { client.emit('connected'); client.on('init', function(req) { client.set('room', req.room); client.set('name', req.name); socket.to(req.room).emit('System', '[' + req.name + '] has come.'); client.emit('System', 'Join [' + req.room + '] as [' + req.name + ']. Enjoy!'); client.join(req.room); client.emit('initialized'); }); client.on('message', function(data) { var obj = {}; getRoomAndNameObj(client, obj); socket.to(obj.room).emit('message', obj.name + ': ' + data); }); client.on('leave', function() { var obj = {}; getRoomAndNameObj(client, obj); if (obj.name) { socket.to(obj.room).emit('System', '[' + obj.name + '] has left..'); } client.leave(obj.room); }); client.on('disconnect', function() { var obj = {}; getRoomAndNameObj(client, obj); if (obj.name) { socket.to(obj.room).emit('System', '[' + obj.name + '] has disconnected..'); } }); });
いろいろ中途半端なのは突っ込んじゃダメです。
所感
そもそも最初に検証したかったのは、Socket.IOもといWebsocketでどんなことができそうかを把握することと、3G回線で使い物になるのかという点。
できそうなこと
やっぱNodeもそうやけど、APIのエンドポイントみたいな使い方がしっくりきそう。
結局Nodeは単純にサーバーやので、どういう使い方するか・・やけど。
Httpに代わるWebsocketという意味では、いろいろアイデアが沸いてきました。
やっぱTwitterのアレとLocationのアレでアレしてみたい。
3Gで
個人的に収穫だと思ったのはこっち。
一旦コネクションを張るまではやっぱ遅いんですけど、繋いでからは上々かと。
アップロードはモタつきがあったりSocket.IOさまのお力に頼りまくりですが、ダウンロードは早かったです。
今回のチャットのサンプルでも、閲覧専用にしてしまえばストレスなくスイスイでした。
もっと最適化できる部分もあると思いますが、個人的には満足。
サーバー側でエグい処理をさせて、結果だけを落とすってパターンで何か作ってみようと画策中です。
PCは?って言われると・・早いけどAjaxとかでも良いんじゃ・・と思ってしまう。
リアルタイム性は置いておくならば。