Socket.IOのチャットアプリで誰が入力中なのかを出力したい


前回までで簡易的なチャットアプリを作成することができた。

この時点ではチャットのページを知っている人は誰でも参加することができてしまうため、

AさんとBさんのみといったユーザを限定したチャットではない。


直近の目標はLineやChatWorkのようにユーザを限定したメッセージのやりとりなので、

ユーザを限定する方法というものを模索してみる。




Socket.IOにはNamespacesとRoomsいう概念がある。

簡単にまとめると、

Namespacesは機能毎にチャットを作る。chatとかnewsとか

Roomsはチャット内でカテゴリ分けする。

Socket.IO — Rooms and Namespaces


詳細は特に触れずに簡単な接続実験を行ってみたいと思う。

※実験自体は次回に記載する


前回まで作成してきた各ファイルを下記のように書き換える。


server.js

var fs = require("fs");
var http = require("http");
var server = http.createServer();

server.on("request", function(request, response){
	var stream = fs.createReadStream("index.html");
 	response.writeHead(200, {"Content-Type":"text/html"});
 	stream.pipe(response);
});

server.listen(8080);
var io = require("socket.io").listen(server);

// ユーザ管理ハッシュ
var userHash = {};

// Namespacesを利用する
var chatNS = io.of('/chat');
chatNS.on("connection", function(socket){

	// Room(Namespacesで分けた時、roomも利用した方が良いみたい)
	var roomName = "default";

	// WebSocketで接続の際にどのroomに参加するか?
	socket.join(roomName);

	// 接続開始のカスタムイベント(接続元ユーザを保存し、他ユーザへ通知)
	socket.on("connected", function(name){
		var msg = name   "が入室しました";
		userHash[socket.id] = name;
		chatNS.to(roomName).emit("pushlish", {value: msg});
	});

	// メッセージ送信カスタムイベント
	socket.on("publish", function(data){
		chatNS.to(roomName).emit("publish", {value:data.value});
	});

	let nowTyping = 0;
	socket.on("start typing", function(){
		if (nowTyping <= 0) {
			socket.to(roomName).emit("start typing", userHash[socket.id]);
		}

		nowTyping++;
		setTimeout(function(){
			nowTyping--;
			if (nowTyping <= 0) {
				socket.to(roomName).emit("stop typing");
			}
		}, 3000);
	});

	socket.on("stop typing", function(){
		nowTyping = 0;
		socket.broadcast.emit("stop typing");
	});

	// 接続終了組み込みイベント(接続元ユーザを削除し、他ユーザへ通知)
	socket.on("disconnect", function(){
		if(userHash[socket.id]){
			var msg = userHash[socket.id] + "が退出しました";
			delete userHash[socket.id];
			chatNS.to(roomName).emit("publish", {value: msg});
		}
	});
});

Namespacesを利用する時にvar chatNS = io.of("/chat");とすることで、機能別に分けることができ、

connectionイベントの実行の際に今までio.on("connection", 処理);としていた箇所を、

chatNS.on("connection", 処理);とすることで個別に決めたnamespaceで動作するようになる。


この時、他にもあったio.on(イベント, 処理);をchatNS.on(イベント, 処理);とすることで、

各namespace内で動作するイベントとして登録することができる。


各ブラウザがサーバに接続した直後に、socket.join(room名)でroomに参加させ、

chatNS.to(room名).emit(イベント名);で更にroomという単位で処理を指示することができる。

※カテゴリ分けが1つであっても、namespacesを利用している際にroomを指定しないと意図しない動作がいろいろと見られたので、room一つでも処理は入れておいが方が良さそう。


続いて、フロント側で、index.htmlの修正箇所は

var socket = io.connect("http://localhost:8080");

var socket = io.connect("http://localhost:8080/chat");

とするだけで良い。


この状態で実行してみると、



今まで通り問題なく動作している。

namespaceは一つだけなので、今の所はこれで問題ない。