先日、LineのようなチャットアプリをSOY Shopのマイページに組み込めませんか?

と質問があったため、チャットアプリのことを調べてみた。


最初に書いておくと、リアルタイムでやりとりを行えるチャットアプリの開発はSOY Shopで使用されているPHPという言語では面倒らしく、

PHP - Wikipedia


Node.js


リアルタイムが得意なサーバサイドのJavaScriptであるNode.jsが得意らしい。

よって、今回開発したチャットアプリを標準実装するのは難しい。

JavaScript - Wikipedia


というわけで、チャットアプリの箇所はNode.jsで書くことにする。




リアルタイムなやりとり(ブラウザで表示している画面を更新することなく、メッセージを送信したり受信したりできる仕組み)を行う上で、HTML5のWebSocketというAPIを利用すればスマートに実装できるのだが、

WebSocketに対応していないブラウザというものが今でもあるらしく、

非対応ブラウザとのかき分けがいろいろと大変。

WebSocket - Wikipedia


そんな中で調べてみたのが、


Socket.IO


Node.jsのライブラリであるSocket.IOがそこら辺を良い感じに対応してくれる。

というわけで、Node.js + Socket.IOでリアルタイムのチャットアプリを作成してみることにしよう。




最初にNode.jsをインストールしてみる。

今回説明で使用している環境はUbuntu 17.04です。


端末を開き、下記のコマンドでNode.jsとnpmというパッケージマネージャをインストールする。

npm

sudo apt-get install nodejs npm
# Node.jsとnpmがインストールされているか調べる
node -v
npm -v

上記の方法でNode.jsはインストールされるが、若干古いバージョンなので、

新しいバージョンをインストールしたい場合は自身で調べて下さい。




続いて、npmを利用して、Socket.IOをインストールします。

Socket.IOのインストールは端末で下記のコマンドを実行する。

npm install socket.io

これで諸々の準備は終了。




早速、リアルタイムのチャットアプリのコードを書いてみる。

コードは下記のサイトを参考にしました。

[Node.js] Socket.ioで双方向通信チャットアプリを構築 〜 JSおくのほそ道 #005 - Qiita


ファイルの配置は下記の通り

workspace
├── index.html
└── server.js

先にserver.jsの方を書くと、

var fs = require("fs");
var http = require("http");
var server = http.createServer();
server.on("request", function(request, response){
	//HTMLファイルをストリームで読み込む
	var stream = fs.createReadStream("index.html");
	response.writeHead(200, {"Content-Type":"text/html"});
	stream.pipe(response);
});
server.listen(8080);

// HTTPをWebSocketにUpgradeする
var io = require("socket.io").listen(server);

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

// チャットアプリに接続した時に実行される
io.on("connection", function(socket){

	/** socketは接続しているブラウザで、以下のコードは接続直後にイベントを登録していく **/

	// ブラウザ毎の接続開始周り。
	socket.on("connected", function(name){
		var msg = name + "が入室しました";
		userHash[socket.id] = name;
		io.emit("pushlish", {value: msg});
	});

	// メッセージ送信のイベント
	socket.on("publish", function(data){
		io.emit("publish", {value:data.value});
	});

	// ブラウザを閉じた等で退出イベント
	socket.on("disconnect", function(){
		if(userHash[socket.id]){
			var msg = userHash[socket.id] + "が退出しました";
			delete userHash[socket.id];
			io.emit("publish", {value: msg});
		}
	});
});

続いて、index.htmlは

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>web socket</title>
</head>
<body>
<input type="text" id="msg_input" style="width:200px;">
<button onclick="publishMessage();">語る</button>
<div id="msg"></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
<script>
//イベントとコールバックの定義
var socket = io.connect("http://localhost:8080");

socket.on("connected", function(name){});
socket.on("publish", function(data){ addMessage(data.value);});
socket.on("disconnect", function(){});

var msgArea = document.querySelector("#msg");
var myName = Math.floor(Math.random()*100) + "さん";
addMessage("貴方は" + myName + "として入室しました");
start(myName);

// チャットアプリのサーバ側に接続する
function start(name){
	socket.emit("connected", name);
}

// メッセージの送信
function publishMessage(){
	var textInput = document.querySelector("#msg_input");
	var msg = "[" + myName + "] " + textInput.value;
	socket.emit("publish", {value: msg});
	textInput.value = "";
}

// 自身の送信時やサーバ側から受信した際のメッセージを表示する
function addMessage(msg){
	var domMsg = document.createElement("div");
	domMsg.innerHTML = new Date().toLocaleTimeString() + " " + msg;
	msgArea.appendChild(domMsg);
}
</script>
</body>
</html>

ものすごい雑な説明になるけれども、server.jsとindex.htmlの各所にio.on(イベント名, 処理)やsocket.emti(イベント名,処理)があるけれども、

複数人でチャットアプリに接続している時、誰かの端末でsocket.emit(イベント名, 処理)をすると、同じイベント名のio.on(イベント名, 処理)の処理の箇所が実行される。

emitメソッドとonメソッドを関連させることでリアルタイムなやりとりを実現することになる。


socket.ioのフロント側のjsファイルはCDNで取得しているけれども、

npmでsocket.ioをインストールすると、node_modulesディレクトリ内にダウンロードされるので、それを使っても良い。


それでは、作成したコードを実行してみる。

端末を開き、下記のコードを実行し、

node server.js

複数のブラウザで開いてみると、

※今回はlocalhost:8080をチャットアプリのサーバにしているため、ブラウザでhttp:localhost:8080にアクセスしてみます。



意図通り、

どちらかのブラウザで何らかの文字列を投稿すると、両方のブラウザで同じ内容が表示されるようになった。


ただ、

これだけではLineのようなアプリまでは程遠いので、

次回以降でSocket.IOで何ができるのかを見ていくことにする。