WebSocket
https://gyazo.com/41db5741e5ef1be969d14b653d8f3daa https://hpbn.co/primer-on-browser-networking/
Webにおける双方向通信を低コストで行うための規格
サーバーとクライアントは一対一の通信をする
コネクションが確立すれば、クライアント/サーバーのどちらからでもデータの送受信ができる
相手は決まっているので、送り先などの情報を含めない
プロトコルの名称なのかmrsekut.icon
一回切れたら、接続し直さないといけない?
通信の仕組み
手順
HTTPでクライアントとサーバー間で情報をやり取りしてコネクションを確立
確立したコネクション上で、低コストな双方向通信
ここではHTTPを使わない?
利用例
チャットアプリを作る
push通知は?これ?
memo
今までは、クライアントがデータを取得するためには、クライアントからサーバーへリクエストを送ってレスポンスをもらわないといけなかったが、そうじゃなくて、サーバーから能動的にクライアントに何かをpushできるようになったってこと?
HTTPにもロングポーリングというのを使って無理やり同じようなことができたが、無駄が多すぎるらしい 無駄な通信が多いってことか?
用語
送信するデータの最小単位?
中身は、ビット列
クライアントの準備
めっちゃかんたんmrsekut.icon
WebSocketクラスを使う
code:js
var socket = new WebSocket('ws://game.example.com:12000/updates');
socket.onopen = () => {
setInterval(() => {
if (socket.bufferedAmount === 0) {
socket.send(getUpdateData());
}
}, 50);
}
URIスキーム
wsはWebSocketのことだよmrsekut.icon
wssというsecureなやつ?もある
メソッド
send([データ])
データをサーバーに送信
onmessage
イベントハンドラでデータを受信
clode([コード[,理由]])
ソケット切断
データに扱えるデータ型
文字列
Blob
サーバーの準備
従来の何かとはどう違う?
どんな技術を使っている?
利用したければ何を使う必要がある?
通信とか
ライブラリとか
関連
のhooks
参考
ざっくり紹介されていて、概要を掴むには良い記事
7章で理論の解説
9章でGoでの実装
よんでない
ガチめ
くわしい
仕様の非公式日本語訳
ChatGPTに聞いてとりあえずうごくものができた
全くコード読んでないので、何も理解してないけど
$ node srver.js
code:server.js
const WebSocket = require("ws");
const { v4: uuidv4 } = require("uuid");
const clients = [];
const server = new WebSocket.Server({ port: 8080 });
server.on("connection", (socket) => {
console.log("Connected");
const id = uuidv4();
const client = { id, socket };
clients.push(client);
socket.on("message", (message) => {
console.log(Received: ${message});
const parsedMessage = JSON.parse(message.toString());
const text = parsedMessage.text;
clients.forEach((c) => {
if (c !== client) {
c.socket.send(JSON.stringify({ clientId: id, text }));
}
});
});
socket.on("close", () => {
console.log("Disconnected");
const index = clients.findIndex((c) => c.id === id);
clients.splice(index, 1);
});
socket.send(JSON.stringify({ clientId: id, text: "" }));
clients.forEach((c) => {
if (c !== client) {
socket.send(JSON.stringify({ clientId: c.id, text: "" }));
}
});
});
create-react-appなどをしたあと
code:ts
import React, { useEffect, useState } from "react";
class WebSocketClient {
private socket: WebSocket | null = null;
private onMessageCallbacks: ((message: MessageEvent) => void)[] = [];
private onOpenCallbacks: (() => void)[] = [];
public connect() {
const socket = new WebSocket("ws://localhost:8080");
socket.addEventListener("open", () => {
console.log("Connected");
this.socket = socket;
this.onOpenCallbacks.forEach((callback) => callback());
});
socket.addEventListener("message", (event) => {
this.onMessageCallbacks.forEach((callback) => callback(event));
});
socket.addEventListener("close", () => {
console.log("Disconnected");
this.socket = null;
});
}
public close() {
if (this.socket) {
this.socket.close();
}
}
public getId(): string {
return this.socket?.url ?? "";
}
public send(message: string) {
if (this.socket) {
this.socket.send(message);
}
}
public onMessage(callback: (message: MessageEvent) => void) {
this.onMessageCallbacks.push(callback);
}
public onOpen(callback: () => void) {
this.onOpenCallbacks.push(callback);
}
}
const webSocketClient = new WebSocketClient();
interface Message {
clientId: string;
text: string;
}
interface Client {
id: string;
name: string;
}
function App() {
const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const newText = e.target.value;
setText(newText);
webSocketClient.send(
JSON.stringify({ clientId: webSocketClient.getId(), text: newText })
);
};
useEffect(() => {
webSocketClient.onMessage((message) => {
console.log(Received message: ${message.data});
try {
const parsedMessage = JSON.parse(message.data.toString()) as Message;
setMessages((messages) => {
const existingMessageIndex = messages.findIndex(
(m) => m.clientId === parsedMessage.clientId
);
if (existingMessageIndex !== -1) {
return newMessages;
} else {
}
});
} catch (error) {
console.error(error);
}
});
webSocketClient.onOpen(() => {
const id = webSocketClient.getId();
webSocketClient.send(JSON.stringify({ clientId: id, text: "" }));
});
webSocketClient.connect();
return () => {
webSocketClient.close();
};
}, []);
useEffect(() => {
setClients((clients) => [
...clients,
{ id: webSocketClient.getId(), name: "You" },
...messages.map((m) => ({
id: m.clientId,
name: User ${m.clientId.substr(0, 4)},
})),
]);
return (
<div>
{/* <div>
{clients.map((client) => (
<div key={client.id}>{client.name}</div>
))}
</div> */}
<div>
<textarea
value={text}
onChange={handleTextChange}
style={{ width: "100%", height: "400px" }}
/>
</div>
<div>
{messages.map((message) => (
<div key={message.clientId}>
<span>{`[${
clients.find((client) => client.id === message.clientId)?.name
}]`}</span>
<span>{message.text}</span>
</div>
))}
</div>
</div>
);
}
export default App;