Docker × socket.io でroomsチャットを作成
はじめに
Docker × socket.ioの記事がない!!
roomsの作り方もわかんない!!
ということで一日クオリティで組んだコードを、コピペするだけで使えるようにかるーーーーく解説。
記事内の解説には誤りがある場合がございます。ご了承ください。
Dockerの仕組みや、socket.ioについてはもっと詳しい記事が多いため割愛。
また、roomsとは部屋割りのことです。
indexページで入力した名前とroomIdをchatページにURLパラメータとして渡すことで実装。
環境構築
node環境構築済みのかたは飛ばしていただいて構いません。
ディレクトリ構成
root
├ dockerfile
└ docker-compose.yml
とりあえずこれだけあれば大丈夫です。
FROM node:14.15.3
WORKDIR /usr/src/app
COPY . .
RUN yarn
version: '3'
services:
app:
build: .
volumes:
- ./app:/usr/src/app
ports:
- 3000:3000
command: sh -c 'node server.js'
tty: true
以上をコピペしてもらえればOKです!
nodeのバージョンは安定板なら基本なんでも大丈夫なはずです。
docker-compose build
docker-compose run --rm app yarn add body-parser express socket.io
これで必要なライブラリのインストールは完了です。
yarnかnpmは個人の好みでお願いします。
コード
ディレクトリ構成
app
├ node_modules
├ public/
| ├ js/
| | ├ room.js
| | └ socket.js
| |
| ├ chat.html
| └ index.html
|
├ package.json
├ server.js
└ yarn.lock
server.jsとpublicフォルダを作成します。
server.jsはexpressによるルーティングと、ソケットのサーバーサイドを書きます。
public内は画面に表示するhtmlとフロントサイドのソケットです。
コード
const express = require("express");
const app = express();
const http = require("http").createServer(app);
const io = require("socket.io")(http);
let store = {};
app.use(express.static('public'));
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
app.get("/chat", (req, res) => {
res.sendFile(__dirname + "/public/chat.html");
});
io.on("connection", (socket) => {
socket.on("join", (msg) => {
usrobj = {
'room': msg.roomId,
'name': msg.name
}
store = usrobj;
socket.join(msg.roomId);
})
socket.on("post", (msg) => {
io.to(store.room).emit("message", msg);
});
});
http.listen(3000, () => {
console.log("success listen 3000");
})
const socket = io();
const room = document.getElementById("room");
const user_name = document.getElementById("user_name");
document.getElementById("room-post").addEventListener("submit", (e) => {
e.preventDefault();
if (user_name.value === "") {
user_name.value = "user1";
}
room.value === "" ? (room.value = 0000) : null;
window.location.href =
window.location +
"chat?roomId=" +
parseInt(room.value) +
"&name=" +
user_name.value;
});
const socket = io();
const url = new URL(window.location.href);
const params = url.searchParams;
const urlRoomId = parseInt(params.get('roomId'));
const urlName = params.get('name');
socket.on("connect", () => {
socket.emit("join", { roomId: urlRoomId, name: urlName });
})
document.getElementById("frm-post").addEventListener("submit", (e) => {
e.preventDefault();
const msg = document.getElementById("msg");
if (msg.value === "") {
return false;
}
socket.emit("post", { text: msg.value });
msg.value = "";
});
socket.on("message", (msg) => {
const list = document.getElementById("msglist");
const li = document.createElement("li");
li.innerHTML = `${msg.name}:${msg.text}`;
list.insertBefore(li, list.firstChild);
});
window.addEventListener('load', () => {
msg.focus();
})
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>SocketIOチャット:RoomId</h1>
<form id="room-post">
<label>
<h2>RoomId</h2>
<input type="text" id="room" maxlength="4" placeholder="1234">
<label>
name
<input type="text" id="user_name" name="name" value="user1">
</label>
</label>
<input type="submit" value="入室">
</form>
<script src="socket.io/socket.io.min.js"></script>
<script src="./js/room.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SocketIOチャット</title>
</head>
<body>
<h1>SocketIOチャット</h1>
<form id="frm-post">
<label>
comment
<input type="text" id="msg" autocomplete="off">
</label>
<button>送信</button>
</form>
<ul id="msglist">
</ul>
<script src="socket.io/socket.io.min.js"></script>
<script src="./js/socket.js"></script>
</body>
</html>
これをコピペするだけで完成するので、あとは
docker-compose up
で起動します。
ここに接続するだけでおわり。完成イメージ
解説
htmlはformを置くだけなので割愛。
server.js
const express = require("express");
const app = express();
const http = require("http").createServer(app);
const io = require("socket.io")(http);
必要なライブラリを呼び出しています。
let store = {};
空の配列を作成。後に使います。
app.use(express.static('public'));
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
app.get("/chat", (req, res) => {
res.sendFile(__dirname + "/public/chat.html");
});
expressのルーティングです。app.useでルートフォルダをpublicに指定しています。
これがないとhtmlからjsファイルやcssファイルを読み込めません。
app.getでそのURLにアクセスがあったときに表示するファイルを選択しています。
io.on("connection", (socket) => {
});
connection
イベントでソケット通信を開始時の処理をします。
socket.on("join", (msg) => {
usrobj = {
'room': msg.roomId,
'name': msg.name
}
store = usrobj;
socket.join(msg.roomId);
})
socket.on("post", (msg) => {
io.to(store.room).emit("message", msg);
});
.on
は受信。.emit
は送信の処理をしています。
join
イベントを受け取ることでroomに入る処理を行います。
socket.join
の引数で共通のroomに入室できます。
post
イベントでメッセージの送信を行います。
io.to
に指定した引数の部屋に引数msgを送信しています。
http.listen(3000, () => {
console.log("success listen 3000");
})
localhost:3000でサーバーをたて、成功した場合console.log
で出力しています。
room.js
ここからはsocket部分に絞って解説
window.location.href =
window.location +
"chat?roomId=" +
parseInt(room.value) +
"&name=" +
user_name.value;
入力されたroomIdとnameをURLパラメータとして生成し、遷移。
indexはこれだけ。
chat.js
const socket = io();
const url = new URL(window.location.href);
const params = url.searchParams;
const urlRoomId = parseInt(params.get('roomId'));
const urlName = params.get('name');
1行目でsocket.ioを呼び出しているのではなく、ソケット通信が開通されるらしいです。
初見じゃわからない。
下4行はURLパラメータの取得です。
socket.on("connect", () => {
socket.emit("join", { roomId: urlRoomId, name: urlName });
})
connect
イベントでソケットに接続します。
join
イベントの引数にroomIdとnameを指定します。投稿はこの時にサーバに送信された名前で行われます。また、このroomIdが同じ人とチャットが可能です。
e.preventDefault();
jsの関数で、formの画面移動のかかる挙動を無視します。
const msg = document.getElementById("msg");
if (msg.value === "") {
return false;
}
入力欄が空の場合メッセージが送信されないようにしています。
socket.emit("post", { text: msg.value });
postイベントでメッセージを送信します。
socket.on("message", (msg) => {
const list = document.getElementById("msglist");
const li = document.createElement("li");
li.innerHTML = `${msg.name}:${msg.text}`;
list.insertBefore(li, list.firstChild);
});
メッセージを受け取りリストタグで表示させています。
ここのmessage
イベント名はバック側と同じになるように決めてください。
最後に
コードをアコーディオンにしているので、見逃している方は気を付けてください。
TECHCAFE×TAIRのアドベントカレンダー用に作成しました記事です。専門がWEBアプリのため、TAIRとは関わりがないです。お許しください。
Discussion