node.jsのsocket.ioでチャット
nodejsでwebsocket
nodejsのsocket.ioでWebSocketができたのでメモ
仕様
node.jsのsocket.ioで簡易なチャットを作る。
チャットルームに同時に入れるのは2人までとする。
事前準備
・npmでsocket.ioをインストールする
npm install socket.io
○コーディング
var server = require("http").createServer(function(req, res){ res.writeHead(200, {"Content-Type":"text/html"}); var output = fs.readFileSync("./socket_test.html", "utf-8"); res.end(output); }).listen(8888);
requireでhttpオブジェクトをロードし、createServerでserverオブジェクトを作成している。serverは8888ポートでlistenにしておき、リクエストを受けたらfs.readFileSyncで指定したファイルを返している。
var io = require("socket.io").listen(server);
socket.ioをロードし、サーバーにsocket.ioの機能を追加している。
io.sockets.on("connection", function(socket) { console.log("connection connected socket-id" + socket.id); socket.join(socket.id);
クライアント側からのsocket接続リクエストを受け取っている。socket.idによりuserの管理を行うことができる。
クライアント側でsocket接続のリクエストを飛ばしているのは以下になる。
<script src="/socket.io/socket.io.js"></script> <script type="text/javascript"> var socketio = io.connect('http://nodejs_no_server:8888');
socket.on("roomSelect", function(data){ console.log("roomSelect"); console.log("roomClient" + io.sockets.manager); var name = data.name; var room = data.room; console.log("name:" + name + "; room" + room); if(room.match(/[^0-9]+/)){ io.sockets.to(socket.id).emit("result", {result:0, msg:"フォーマットエラー"}); console.log("フォーマットエラー"); return; } if(roomStateHash[room] == undefined) roomStateHash[room] = 0; console.log("room" + room +" state:" + roomStateHash[room]); if(roomStateHash[room]>=2){ io.sockets.to(socket.id).emit("result", {result:0, msg:"満室です"}); return; }else{ userHash[socket.id] = name; userRoomHash[socket.id] = room; if(roomStateHash[room] == null || roomStateHash[room] == 0){ roomStateHash[room] = 1; }else{ roomStateHash[room] += 1; } var html = fs.readFileSync("./socket_game.html", "utf-8"); io.sockets.to(socket.id).emit("result", {result:1, msg:"あなたは room" + room + "に入室しました。", html:html, }); } socket.join(userRoomHash[socket.id]); io.sockets.to(userRoomHash[socket.id]).emit("publish", {value:userHash[socket.id] + "がroom" + userRoomHash[socket.id] + "に入室しました"}); });
socket.on("roomSelect", function(data)ではクライアント側からのroomSelectのリクエストを
受け取っている。
クライアントサイドでroomSelectリクエストを飛ばしているのは以下になる。
function selectRoom(){ console.log("select Room"); var data = {}; var name = document.getElementById("name_input"); var room = document.getElementById("room_input"); data.name = name.value; data.room = room.value; socketio.emit("roomSelect", data); } }
socketio.emit("roomSelect", data);で部屋の名前と自分の名前をリクエストのデータに渡している。
チャットルームの管理には以下のグローバル変数を使っている。
var userHash = {};
var userRoomHash = {};
var roomStateHash = {};
userHashにはsocket.idを引数にしてユーザー名を入れている。var userRoomHashにはsocket.idを引数にユーザーのチャットルーをを入れている。
roomStateHashにはチャットルームの人数が入るようにしている。
同室内のコメントのみをemitするためにはサーバーサイドで以下の処理を行う。
socket.join(userRoomHash[socket.id]);
io.sockets.to(userRoomHash[socket.id]).emit("publish",
{value:userHash[socket.id] + "がroom" + userRoomHash[socket.id] + "に入室しました"});
部屋番号を指定joinした上でそこにemitしている。
クライアントサイドでemitを受け取る処理は以下になる。
socketio.on("publish", function(data){addMessage(data.value);});
これで、サーバーサイドで"publish"指定のemitを受け取ったらクライアントサイドではaddMessageメソッドを実行するようになる。
コード全体は以下になる。
サーバーサイド
var fs = require("fs"); var server = require("http").createServer(function(req, res){ res.writeHead(200, {"Content-Type":"text/html"}); var output = fs.readFileSync("./socket_test.html", "utf-8"); res.end(output); }).listen(8888); console.log("server running port 8888"); var io = require("socket.io").listen(server); var userHash = {}; var userRoomHash = {}; var roomStateHash = {}; io.sockets.on("connection", function(socket) { console.log("connection connected socket-id" + socket.id); socket.join(socket.id); socket.on("roomSelect", function(data){ console.log("roomSelect"); console.log("roomClient" + io.sockets.manager); var name = data.name; var room = data.room; console.log("name:" + name + "; room" + room); if(room.match(/[^0-9]+/)){ io.sockets.to(socket.id).emit("result", {result:0, msg:"フォーマットエラー"}); console.log("フォーマットエラー"); return; } if(roomStateHash[room] == undefined) roomStateHash[room] = 0; console.log("room" + room +" state:" + roomStateHash[room]); if(roomStateHash[room]>=2){ io.sockets.to(socket.id).emit("result", {result:0, msg:"満室です"}); return; }else{ userHash[socket.id] = name; userRoomHash[socket.id] = room; if(roomStateHash[room] == null || roomStateHash[room] == 0){ roomStateHash[room] = 1; }else{ roomStateHash[room] += 1; } var html = fs.readFileSync("./socket_game.html", "utf-8"); io.sockets.to(socket.id).emit("result", {result:1, msg:"あなたは room" + room + "に入室しました。", html:html, }); } socket.join(userRoomHash[socket.id]); io.sockets.to(userRoomHash[socket.id]).emit("publish", {value:userHash[socket.id] + "がroom" + userRoomHash[socket.id] + "に入室しました"}); }); socket.on("connected", function(name){ console.log("connected"); var msg = name + "が入室しました"; userHash[socket.id] = name; io.sockets.emit("publish", {value: msg}); }); socket.on("publish", function(msg){ console.log("publish msg:" + msg); io.sockets.to(userRoomHash[socket.id]).emit("publish", {value:userHash[socket.id] + ":" + msg}); }); socket.on("disconnect", function(){ console.log("disconnect"); if(userHash[socket.id]){ var msg = userHash[socket.id] + "が退出しました"; io.sockets.to(userRoomHash[socket.id]).emit("publish", {value:msg}); if(roomStateHash[userRoomHash[socket.id]] != undefined){ roomStateHash[userRoomHash[socket.id]] -= 1; } console.log("room" + userRoomHash[socket.id] + "state:" + roomStateHash[userRoomHash[socket.id]]); delete userRoomHash[socket.id]; delete userHash[socket.id]; } }); });
クライアントサイド
<body> <div id="initArea"> <h1>名前入力</h1> <input type="text" id="name_input" style="width:200px;" /> <h1>部屋選択</h1> <input type="text" id="room_input" style="width:30px;" /> <button onclick="selectRoom();">選択</button> </div> <div id="gameArea" style="display:none"> </div> <div id="chatArea" style="display:none"> <button onclick="publishMessage();">語る</button> <input type="text" id="msg_input" style="width:200px;" /> <div id="msg"></div> </div> <script src="/socket.io/socket.io.js"></script> <script type="text/javascript"> var socketio = io.connect('http://www16436ui.sakura.ne.jp:8888'); socketio.on("result", function(data){result(data);}); socketio.on("connected", function(name){}); socketio.on("publish", function(data){addMessage(data.value);}); socketio.on("disconnect", function(){}); function selectRoom(){ console.log("select Room"); var data = {}; var name = document.getElementById("name_input"); var room = document.getElementById("room_input"); data.name = name.value; data.room = room.value; socketio.emit("roomSelect", data); } function result(data){ if(data.result == 0){ alert(data.msg); }else{ var initArea = document.getElementById("initArea"); initArea.setAttribute("style", "display:none"); var chatArea = document.getElementById("chatArea"); chatArea.setAttribute("style", "dispaly:"); var gameArea = document.getElementById("gameArea"); gameArea.innerHTML = data.html; gameArea.setAttribute("style", "display:"); var domMsg = document.createElement('div'); domMsg.innerHTML = new Date().toLocaleTimeString() + ' ' + data.msg; msgArea.appendChild(domMsg); } } function start(name){ socketio.emit("connected", name); } function publishMessage(){ var textInput = document.getElementById('msg_input'); socketio.emit("publish", textInput.value); textInput.value=''; } function addMessage(msg){ var domMsg = document.createElement('div'); domMsg.innerHTML = new Date().toLocaleTimeString() + ' ' + msg; //msgArea.appendChild(domMsg); msgArea.insertBefore(domMsg, msgArea.firstChild); } var msgArea = document.getElementById("msg"); //var myName = Math.floor(Math.random()*100) + "さん"; //addMessage("あなたは" + myName + "として入室しました"); //start(myName); </script> </body> </html>