WebSocketを触る

今回は、WebSocket を触ってみます。
調べつつ、よくあるチャットページを作ってみます。


目次

参考

WebSocket とは

そもそも WebSocket とは何なのか。
HTTP 通信の場合は、クライアントのリクエストに基づき、サーバーがレスポンスを返します。
リソースを要求すると、都度コネクションを作るプロトコルです。

WebSocket は、コネクションを確立するとクライアント・サーバー間でコネクションが確立すると、双方向の通信ができる。
ポイントは、「双方向」。
HTTP と異なり、サーバーからクライアントに情報をプッシュできる。

今回やること

今回は、「Socket.IO」を使って、よくあるチャットを作ってみます。

実装 1

ディレクトリ構成

今回作成したプログラムのディレクトリ構成は、以下のようになります。
server.jsindex.htmlの 2 つを作成します。

1
2
3
4
5
6
.
+---public
| +---index.html #クライアントのページ
+---node_modules
\---src
+---server.js #サーバープログラム

サーバー側 1

server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const path = require("path");

const app = require("express")();
const http = require("http");
const server = http.Server(app);
const io = require("socket.io")(server);

//サーバーのポート
const port = process.env.port || 3000;

//httpのレスポンス
app.get("/", (req, res) => {
res.sendFile(path.join(process.cwd(), "public", "index.html"));
});

//WebSocket
io.on("connection", socket => {
socket.on("message", data => {
io.emit("message", data);
});
});

server.listen(port, () => {
console.log(`start Server at port:${port}`);
});

クライアント側 1

css はいつも通り、シンプル一番な、「skeleton」を使います。

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<html>
<head>
<title>chat site</title>
<link
rel="stylesheet"
type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css"
/>
</head>
<body>
<div class="container">
<div>
<input type="text" id="message_text" />
<button type="button" id="message_send">
Send
</button>
</div>
<div id="messages">
<ul id="message_list"></ul>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>

<script>
$(function() {
//socketの作成
var socket = io();

//メッセージの送信
var emit = function() {
if ($("#message_text").val() != "") {
socket.emit("message", $("#message_text").val());
$("#message_text").val("");
}
};

//メッセージの受信
socket.on("message", function(data) {
$("#message_list").prepend($("<li>").text(data));
});

//テキストボックスとボタンの動作
$("#message_text").keypress(function(event) {
if (event.which === 13) {
emit();
}
});
$("#message_send").click(function() {
emit();
});
});
</script>
</body>
</html>

確認 1

node src/server.jsで起動し、ブラウザでlocalhost:3000にアクセスします。
動作の様子は、次のようになります。

2 つのページの間で、入力を共有できました。
収録した動画は、2 つのページですが 3 つ以上でも OK です。

実装 2

今度は、Socket.IOが提供する room を使って、チャットルームを作ってみます。
roomを用いると、メッセージの送信範囲を制限できます。

サーバー側 2

server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
const path = require("path");
const crypto = require("crypto");

const app = require("express")();
const http = require("http");
const server = http.Server(app);
const io = require("socket.io")(server);

//サーバーのポート
const port = process.env.port || 3000;

//httpのレスポンス
app.get("/", (req, res) => {
res.sendFile(path.join(process.cwd(), "public", "index.html"));
});

//WebSocket
io.on("connection", socket => {
//部屋設定
let room = "";
//ユーザー名設定
const time = new Date();
const md5 = crypto.createHash("MD5");
md5.update(time.toString());
let username = md5.digest("hex");

//接続
console.log("New Connection!");
//部屋に入る
socket.on("room_in", data => {
socket.join(data.room);
room = data.value;
io.to(room).emit("message", `${username}が入室`);
});
//部屋を出る
socket.on("room_out", () => {
io.to(room).emit("message", `${username}が退室`);
socket.leave(room);
});
//メッセージの送信
socket.on("message", data => {
io.to(room).emit("message", data);
});
//切断
socket.on("disconnect", function() {
console.log("Disconnected!");
});
});

server.listen(port, () => {
console.log(`start Server at port:${port}`);
});

クライアント側 2

css はいつも通り、シンプル一番な、「skeleton」を使います。
しかし、skeletonでは、disabled がついた要素を見分けにくいので、スタイル を追加しました。

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<html>
<head>
<title>chat site</title>
<link
rel="stylesheet"
type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css"
/>
<style>
select:disabled {
background-color: gray;
}
input:disabled {
background-color: gray;
}
button:disabled {
background-color: gray;
}
</style>
</head>
<body>
<div class="container">
<div>
<select id="select_rooms">
<option value="room1">ROOM01</option>
<option value="room2">ROOM02</option>
<option value="room3">ROOM03</option>
</select>
<button id="room_in_out">in</button>
</div>
<div>
<input type="text" id="message_text" disabled="true" />
<button type="button" id="message_send" disabled="true">
Send
</button>
</div>
<div id="messages">
<ul id="message_list"></ul>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>

<script>
$(function() {
//socketの作成
var socket = io();
var room_state = false;

//メッセージの送信
var emit = function() {
if ($("#message_text").val() != "") {
socket.emit("message", $("#message_text").val());
$("#message_text").val("");
}
};

//メッセージの受信
socket.on("message", function(data) {
$("#message_list").prepend($("<li>").text(data));
});

//テキストボックスとボタンの動作
$("#message_text").keypress(function(event) {
if (event.which === 13) {
emit();
}
});
$("#message_send").click(function() {
emit();
});
$("#room_in_out").click(function() {
var selectRoom = $("#select_rooms").val();
room_state = !room_state;

if (room_state) {
socket.emit("room_in", { room: selectRoom });
//部屋選択UI
$("#room_in_out").text("out");
$("#select_rooms").prop("disabled", true);

//メッセージ送信UI
$("#message_text").prop("disabled", false);
$("#message_send").prop("disabled", false);

//メッセージリストを削除
$("#message_list").empty();
} else {
socket.emit("room_out");
//部屋選択UI
$("#room_in_out").text("in");
$("#select_rooms").prop("disabled", false);

//メッセージ送信UI
$("#message_text").prop("disabled", true);
$("#message_send").prop("disabled", true);

//メッセージリストを削除
$("#message_list").empty();
}
});
});
</script>
</body>
</html>

確認 2

確認 1 と同様にnode src/server.jsで起動し、ブラウザでlocalhost:3000にアクセスします。
動作の様子は、次のようになります。

同じ部屋を指定した時だけ 2 つのページの間で、入力を共有できました。


今回は、WebSocket を使用してみました。
Socket.IO のおかげでかなり簡単に 2 つチャットを作れました。
この前作成した Slackbot のライブラリや mqtt のライブラリで使用されているので使ってはいましたが、使っているだけでした。
理解して活用したいですね。

ではでは。