Node.js, WebSocket(双方向通信) 使って簡易チャットを作ってみる
見た目は某〇INE風にしてます
実行した環境とかは以下です
- Windwos11
- Node.js v18.17.1
- WebSocket v8.14.2 (ws@8.14.2)
- http (http@0.0.1-security)
- http-server (http-server@14.1.1)
あと完成したやつは github においてます
https://github.com/sueasen/chat-sample
プロジェクトフォルダ作成・移動
mkdir chat
cd chat
node 初期化
npm init -y
WebSocket インストール
npm i ws
http インストール
npm i http
http-server インストール
これだけグローバルにインストール
npm i -g http-server
server.js (バックエンド) 作成
フォルダ分けてもOK、ここでは直下に作成していく
やってることはざっくりと以下になる
- WebSocketサーバ作成 (これで双方向通信できる)して
wss
に設定 - 接続時の処理を設定
wss.on('connection', (ws) => {処理}
(ws
が接続したクライアント) - 上の処理の中でクライアントとのやり取りを設定
ws.on('処理名', 処理)
,ws.send(値)
など
server.js
const WebSocket = require('ws');
const http = require('http');
const crypto = require('crypto');
// WebSocketのサーバ作成
const server = http.createServer();
const wss = new WebSocket.Server({ server });
// WebSocket接続, ws が接続したクライアント
wss.on('connection', (ws) => {
// クライアント識別子、今回は使わない...
const uuid = crypto.randomUUID();
ws.send(JSON.stringify({ uuid }));
// メッセージ受信処理
ws.on('message', (data) => {
const json = JSON.parse(data);
if (!json.message) return;
// WebSocket 接続中のクライアント対象にメッセージ送信
wss.clients.forEach((client) => {
// メッセージ送信先クライアントがメッセージ受信クライアントの判定を設定
json.mine = ws === client;
if (client.readyState === WebSocket.OPEN) {
// メッセージを送信
client.send(JSON.stringify(json));
}
});
});
});
server.listen(3000, () => {
console.log('WebSocket Server is running on port 3000');
});
チャット用html, css, js作成(フロントエンド)
フォルダ分けてもOK、ここでは直下に作成していく
チャット用の index.html, index.css, index.js を作成する
WebSocket のところは index.js にまとめる
- index.js
index.js
// WebSocket接続
const ws = new WebSocket('ws://localhost:3000');
let uuid = null;
// メッセージ受信処理
ws.onmessage = (event) => {
const json = JSON.parse(event.data);
console.log(json);
if (json.uuid) {
uuid = json.uuid;
} else {
const chatDiv = document.getElementById('chat');
chatDiv.appendChild(createMessage(json));
chatDiv.scrollTo(0, chatDiv.scrollHeight);
}
};
// メッセージ送信処理
function sendMessage() {
const now = new Date();
const json = {
name: document.getElementById('nameInput').value,
message: document.getElementById('msgInput').value,
time: `${now.toLocaleDateString()} ${now.toLocaleTimeString()}`,
};
// メッセージ送信
ws.send(JSON.stringify(json));
document.getElementById('msgInput').value = '';
}
// ここから下はDOM生成処理(メッセージ受信後のDOM生成)
function createMessage(json) {
const side = json.mine ? 'mine' : 'other';
const sideElement = createDiv(side);
const sideTextElement = createDiv(`${side}-text`);
const timeElement = createDiv('time');
const nameElement = createDiv('name');
const textElement = createDiv('text');
timeElement.textContent = json.time;
nameElement.textContent = json.name;
textElement.textContent = json.message;
sideElement.appendChild(sideTextElement);
sideTextElement.appendChild(timeElement);
sideTextElement.appendChild(nameElement);
sideTextElement.appendChild(textElement);
return sideElement;
}
function createDiv(className) {
const element = document.createElement('div');
element.classList.add(className);
return element;
}
- index.html
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WebSocket Client</title>
<link rel="stylesheet" href="index.css" type="text/css" />
</head>
<body>
<div class="container">
<div class="title">チャット</div>
<div class="contents scroll" id="chat">
<div class="contents input">
<div>
<input class="name" type="text" id="nameInput" placeholder="name" />
</div>
<div>
<input class="msg" type="text" id="msgInput" placeholder="message" />
</div>
<button onclick="sendMessage()">Send</button>
</div>
</div>
<script src="index.js"></script>
</body>
</html>
- index.css
index.css
.container {
padding: 0;
background: #7494c0;
overflow: hidden;
max-width: 400px;
margin: 20px auto;
font-size: 80%;
}
/* タイトル部分 */
.container .title {
background: #273246;
padding: 10px;
text-align: center;
font-size: 150%;
color: #ffffff;
}
/* 会話部分 */
.container .contents {
padding: 10px;
overflow: hidden;
line-height: 135%;
}
.container .scroll {
height: 500px;
overflow-y: scroll;
}
/* 相手の会話 */
.container .other {
width: 100%;
position: relative;
display: block;
margin: 5px;
clear: both;
}
.container .other .other-text {
margin-left: 10px;
max-width: 80%;
}
.container .other .other-text .name {
font-size: 80%;
color: #ffffff;
}
.container .other .other-text .time {
font-size: 40%;
color: #ffffff;
}
.container .other .text {
margin: 0;
position: relative;
padding: 10px;
border-radius: 10px 10px 10px 0px;
background-color: #ffffff;
}
/* 自分の会話 */
.container .mine {
width: 100%;
max-width: 80%;
position: relative;
display: block;
margin: 5px;
clear: both;
float: right;
}
.container .mine .mine-text {
margin-right: 10px;
}
.container .mine .mine-text .name {
font-size: 80%;
color: #ffffff;
text-align: right;
}
.container .mine .mine-text .time {
font-size: 40%;
color: #ffffff;
text-align: right;
}
/* コメントエリア */
.container .mine .text {
padding: 10px;
border-radius: 10px 10px 0px 10px;
background-color: #8de055;
margin: 0;
}
/* 入力部分 */
.container .input {
background: #a0a3aa;
padding: 10px;
width: 100%;
}
.container .input input {
padding: 5px;
border-radius: 10px;
border-style: none;
margin-right: 5px;
}
.container .input .name {
width: 20%;
float: left;
}
.container .input .msg {
width: 50%;
float: left;
}
.container .input button {
padding: 5px 10px;
border-radius: 5px;
border-style: none;
margin-right: 5px;
margin-left: 10px;
color: #606165;
}
.container .input button:hover {
opacity: 0.5;
}
server.js 実行(バックエンド)
この後にフロントエンドも実行するので起動しっぱなしにする
ターミナルは二つあげておく
node server.js
http-server 実行(フロントエンド)
ターミナルは先ほどのやつと分ける
こちらも気実行して起動しっぱなしにする
http-server
ブラウザで接続
http://localhost:8080
でチャットのページ開けばOK
name, message 入力して Send 押すと他で開いてる人にメッセージが届く