blog

Node.js + Socket.io による One-to-One ライブチャット

1対1のライブチャットアプリケーションを実現するためには、メッセージをリアルタイムで配信できることが重要ですが、一つの選択肢として、よく知られているWebsocketプロトコルを使用することができます...

Jan 21, 2020 · 6 min. read
シェア

一対一のライブチャットアプリケーションを実現するためには、メッセージをリアルタイムで配信できることが重要ですが、一つの選択肢として、よく知られているWebsocketプロトコルを使用することです。この記事では、Node.jsのフレームワークSocket.ioを使用して実現します。

プレビュー

まずはそれを見てみましょう。実装の最終結果は以下の通りです:

以下の2つのURLをブラウザに入力しても体験できます:

"http://...212:30010"/?sender= &receiver= 
"http://...212:30010"/?sender= &receiver= 

技術の選択

  • フロントエンド:HTML + CSS + JSまた、ページレイアウトといくつかのスタイルレンダリングを実現するためにBoostrapを使用しました。
  • バックエンド: Node.js + Express + Socket.io

フロントエンドの実装

HTML ページレイアウト

チャットページの HTML レイアウトは単純で、以下のように 3 つのレイヤーに分かれています:

  • chat-header: チャットインターフェースのヘッダー情報。
  • chat-content: チャットの全体的なコンテンツ情報を表示するために使用されます。現在表示されているのは、チャットメッセージを送受信する際にメッセージコンテンツをチャット本文に挿入するために DOM を操作するための空の div です。
  • chat-bottom: 下部にはチャットウィンドウのコンテンツ入力ウィンドウと送信ボタンが表示されます。
 <div class="container">
 <div class="chat-header row">
 <span class="col-xs-2 chat-header-left glyphicon glyphicon-menu-left"></span>
 	<span class="col-xs-8 chat-header-center" id="chatHeaderCenter"></span>
 <span class="col-xs-2 chat-header-right glyphicon glyphicon-option-horizontal"></span>
 </div>
 <div class="chat-content" id="chatContent"></div>
 <div class="chat-bottom row">
 <span class="col-xs-10 col-md-11 input-text"><input type="text" class="form-control " id="inputText" placeholder="送信したい内容を入力する..."></span>
 	<span class="col-xs-2 col-md-1 span-submit">
 	<input class="btn btn-default btn-primary input-submit" id="sendBtn" data-dismiss="alert" type="submit" value=" >
 </span>
 </div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="./js/chat.js"></script>

Socket.io Client

クライアントは、最初にソケットオブジェクトを作成し、io()の最初のパラメータは、デフォルトではwindow.locationですリンクされたサーバのURLです。

ソケットクライアントとサーバーは、2つの関数on()、emit()を持っているこれもコアであり、これらの2つの関数を介して簡単にクライアントとサーバー間の双方向通信を実現することができます。

  • emit:イベントをトリガーします。最初のパラメータはイベント名、2番目のパラメータは相手側に送信するデータ、3番目のパラメータは相手側が情報を受信したことを確認するためのコールバック関数です。
  • on: emitがトリガーしたイベントをリッスンするためのイベントを登録します。
// js/chat.js
const socket = io();
socket.on('connect', () => {
 socket.emit('online', query.sender);
});
socket.on('reply_private_chat', replyPrivateMessage);
...

クライアント側でメッセージを送信するには、送信ボタンのonclickイベントやenterイベントをリッスンし、メッセージに何らかの処理を施してsocket.emitでサーバーに送信し、サーバーが別のクライアントに転送します。

より詳細なコードのフロントエンド部分は、ここに記載されていない、あなたは自分自身のために見るためにGithubでダウンCloneすることができ、コード例のアドレスの終わり。

const chatHeaderCenter = document.getElementById('chatHeaderCenter');
const inputText = document.getElementById('inputText');
const sendBtn = document.getElementById('sendBtn');
chatHeaderCenter.innerText = query.receiver;
sendBtn.onclick = sendMsg;
inputText.onkeydown = sendMsgByEnter;
function sendMsg() {
 const value = inputText.value;
 if (!value) return alert('Message is required!');
 const message = { sender: query.sender, receiver: query.receiver, text: value };
 socket.emit('private_chat', message, data => {
 renderMessage(data, true);
 });
 inputText.value = '';
}
...

バックエンドの実装

Expressによるサービス構築

Expressで構築したバックエンド・サービスを使用して、ポート30010をリッスンし、クライアント・ページをロードするapp.jsを作成します。

// app.js
const express = require('express');
const app = express();
const path = require('path');
const server = require('http').createServer(app);
const PORT = 30010;
app.use(express.static(path.join(__dirname, '../', 'public')));
server.listen(PORT, () => console.log(`Server is listening on ${PORT}`));

ソケットの紹介.io

上記でシンプルなExpressサービスを構築したので、次はカスタムio.jsを導入します。

// app.js
require('./io.js')(server);

io.jsを作成すると、socket.ioの読み込み時にサーバ側のioオブジェクトが渡され、同期的に接続イベントが登録されます。

オンライン、private_chat、切断イベントもあり、その一部はシステムによって提供され、一部は以下に説明するようにカスタマイズされます。

const _ = require('underscore');
const moment = require('moment');
const userData = require('./users.json');
const USER_STATUS = ['ONLINE', 'OFFLINE'];
const users = {};
module.exports = server => {
 const io = require('socket.io')(server);
 io.on('connection', socket => {
 socket.on('online', ...)
 socket.on('private_chat', ...);
 socket.on('disconnect', ...);
 });
}

通知

on('online')はカスタムイベントで、クライアントがオンラインになった時に発生し、現在のクライアントのユーザ情報を伝え、ユーザとsocket.idのマッピングを確立するためにsocket.idを保存します。socket.idはクライアントが接続を切断して再接続するたびに変更されます。

socket.on('online', username => {
 socket.username = username;
 users[username] = {
 socketId: socket.id,
 status: USER_STATUS[0]
 };
})

送信されたプライベートチャットメッセージの受信

on('private_chat')もカスタムイベントで、クライアントから送信されたメッセージを受信した後、メッセージを処理し、受信者がオンラインかどうかを判断し、オンラインであればsocket.idを通して対応するソケットを見つけ、受信者にメッセージをプッシュします。プライベートチャットの転送で重要なのは、socket.to().emit()です。

socket.on('private_chat', (params, fn) => {
 const receiver = users[params.receiver];
 params.createTime = moment().format('YYYY-MM-DD HH:mm:ss');
 const senderData = _.findWhere(userData, { username: params.sender });
 params.senderPhoto = (senderData || {}).photo;
 if (!params.senderPhoto) {
 const senderLen = params.sender.length;
 params.senderPhotoNickname = params.sender.substr(senderLen - 2)
 }
 fn(params);
 if (receiver && receiver.status === USER_STATUS[0]) {
 socket.to(users[params.receiver].socketId).emit('reply_private_chat', params);
 } else {
 console.log(`${params.receiver}  `);
 // オフラインのメッセージ・プッシュ処理は
 }
});

disconnect

リンクが切断されたときに発生し、reason はクライアントまたはサーバーがリンクを切断した理由を示します。リンクが切断された理由もこのイベントで変更されます。

socket.on('disconnect', reason => {
 if (users[socket.username]) users[socket.username].status = USER_STATUS[1];
});

&

上記の例をDocker Imageとしてパッケージ化しましたので、興味のある方は以下のコマンドで取り出し、ご自身でデプロイしてください。

docker pull docker.io/qufei1993/private-chat-socketio

コードの例:

  • Github:

オンライン体験のデモ

"http://...212:30010"/?sender= &receiver= 
"http://...212:30010"/?sender= &receiver= 
Read next

何?まだ独自ドメイン名を持っていない?

ドメイン名を購入した後、ドメイン名をファイルすることを忘れてはなりません。その際、実名認証のためにファーウェイクラウドに登録する必要があります。 自分の状況を説明するために必要なプロンプトに従って、レコードのために、レコードシステムにログイン華為技術クラウド、ログインします。それは通常、ファイリング中に問題がある場合、顧客サービスの担当者が電話であなたと通信します、正常にファイルするために約20日かかります。 ドメイン名の解決:つまり、ドメイン名をどこに向ける必要があります。必要 ...

Jan 20, 2020 · 2 min read