WebSocketってなにかをこの前勉強した。
で、それをPythonとNext.jsを使って実装してみた。
でも、そのコードで実際に何をやってるかは深く理解できていないので、このメモで深く理解していこうと思う。ww
今回理解するコードはこちら。
"use client";
import { useEffect, useState, useRef } from "react";
export default function Home() {
const [messages, setMessages] = useState([]);
const [isConnected, setIsConnected] = useState(false);
const [input, setInput] = useState(""); // Added input state for sending messages
const socketRef = useRef(null);
useEffect(() => {
const connectWebSocket = () => {
socketRef.current = new WebSocket("ws://localhost:8000/ws");
socketRef.current.onopen = () => {
console.log("WebSocket connection established");
setIsConnected(true);
};
socketRef.current.onmessage = (event) => {
console.log("Message from server:", event.data);
setMessages((prev) => [...prev, event.data]);
};
socketRef.current.onerror = (error) => {
console.error("WebSocket error:", error);
};
socketRef.current.onclose = () => {
console.log("WebSocketサーバとのコネクションをクローズしました");
setIsConnected(false);
};
};
connectWebSocket();
return () => {
if (socketRef.current) {
socketRef.current.close();
}
};
}, []);
const sendMessage = () => {
if (socketRef.current && isConnected) {
socketRef.current.send(input);
console.log(
"WebSocketサーバにメッセージを送信しました。 message -> ",
input
);
setInput("");
} else {
console.error("WebSocketサーバとの通信を確立できません");
}
};
const closeConnection = () => {
if (socketRef.current) {
socketRef.current.close();
console.log("WebSocketサーバとのコネクションをクローズします");
setIsConnected(false);
}
};
return (
<div>
<h1 className="text-3xl font-bold p-4">WebSocket Messages</h1>
<div className="p-4">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Enter a message"
className="border rounded p-2 mr-2"
/>
<button
onClick={sendMessage}
className="bg-blue-500 text-white p-2 rounded"
>
Send
</button>
<button
onClick={closeConnection}
className="bg-red-500 text-white p-2 rounded ml-2"
>
Close Connection
</button>
</div>
<p className="ml-4">
Status: {isConnected ? "Connected" : "Disconnected, reconnecting..."}
</p>
<ul className="ml-4">
{messages.map((msg, index) => (
<li key={index}>{msg}</li>
))}
</ul>
</div>
);
}
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
import asyncio
import logging
from starlette.websockets import WebSocketState
app = FastAPI()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("websocket_server")
@app.get("/")
async def get():
return HTMLResponse("<h1>WebSocket Server is running</h1>")
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
logger.info("クライアントが接続されました")
try:
send_task = asyncio.create_task(send_messages_periodically(websocket))
while True:
data = await websocket.receive_text()
logger.info(f"Received message from client: {data}")
except WebSocketDisconnect:
logger.warning("クライアントが切断されました")
except Exception as e:
logger.error(f"エラーが発生しました: {e}")
finally:
send_task.cancel()
await websocket.close()
logger.info("WebSocket接続が終了しました")
async def send_messages_periodically(websocket: WebSocket):
"""Periodically send messages to the client"""
for i in range(100):
if websocket.application_state != WebSocketState.CONNECTED:
break
await websocket.send_text(f"test text No.{i}")
await asyncio.sleep(0.05)
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
import asyncio
import logging
from starlette.websockets import WebSocketState
app = FastAPI()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("websocket_server")
@app.get("/")
async def get():
return HTMLResponse("<h1>WebSocket Server is running</h1>")
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
logger.info("クライアントが接続されました")
try:
send_task = asyncio.create_task(send_messages_periodically(websocket))
while True:
data = await websocket.receive_text()
logger.info(f"Received message from client: {data}")
except WebSocketDisconnect:
logger.warning("クライアントが切断されました")
except Exception as e:
logger.error(f"エラーが発生しました: {e}")
finally:
send_task.cancel()
await websocket.close()
logger.info("WebSocket接続が終了しました")
async def send_messages_periodically(websocket: WebSocket):
"""Periodically send messages to the client"""
for i in range(100):
if websocket.application_state != WebSocketState.CONNECTED:
break
await websocket.send_text(f"test text No.{i}")
await asyncio.sleep(0.05)
<aside> 💡
まずはサーバ側の実装から理解していく。