2026年1月8日木曜日

超簡単 websocket お試しコード

超簡単 websocket お試しコード

概要

二人専用チャットルームを簡単に作成してみます

環境

  • macOS 15.7.2
  • Python 3.12.11
    • Flask==3.1.2
    • Flask-SocketIO==5.6.0
    • eventlet==0.40.4

インストール

  • pyenv local 3.12.11
  • pipenv install flask flask-socketio eventlet

アプリ

  • vim app.py
from typing import cast

from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit, join_room, leave_room

app = Flask(__name__)

socketio = SocketIO(app, cors_allowed_origins="*")

CHAT_ROOM_ID = "hoge"

# 接続管理(sid を保持)
chat_clients: set[str] = set()


@app.route("/<room_id>")
def chat(room_id: str):
    if room_id != CHAT_ROOM_ID:
        return "Not Found", 404
    return render_template("index.html", room_id=CHAT_ROOM_ID)


@socketio.on("connect")
def on_connect():
    sid = cast(str, request.sid)  # type: ignore

    if len(chat_clients) >= 2:
        emit("full", to=sid)
        return False

    chat_clients.add(sid)
    join_room(CHAT_ROOM_ID)

    if len(chat_clients) == 1:
        # 1人目
        emit("status", "待っています…", to=sid)
    else:
        # 2人目が入った瞬間に「全員」に通知
        emit("status", "準備OK!", to=CHAT_ROOM_ID)


@socketio.on("message")
def on_message(data):
    msg = data.get("message")
    if msg:
        emit("message", msg, to=CHAT_ROOM_ID, include_self=False)


@socketio.on("disconnect")
def on_disconnect():
    sid = cast(str, request.sid)  # type: ignore
    chat_clients.discard(sid)
    leave_room(CHAT_ROOM_ID)

    if len(chat_clients) == 1:
        # 残った1人に通知
        emit("status", "終わり", to=CHAT_ROOM_ID)


if __name__ == "__main__":
    socketio.run(app, host="0.0.0.0", port=5000, debug=False)

テンプレート

  • vim templates/index.html
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>Private Chat</title>
    <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
  </head>
  <body>
    <h3>Private Chat</h3>
    <div id="status"></div>
    <ul id="log"></ul>

    <input id="msg" placeholder="message" />
    <button onclick="send()">Send</button>

    <script>
      const socket = io();

      socket.on("status", (msg) => {
        document.getElementById("status").innerText = msg;
      });

      socket.on("message", (msg) => {
        const li = document.createElement("li");
        li.textContent = msg;
        log.appendChild(li);
      });

      socket.on("full", () => {
        alert("このチャットは満員です");
      });

      function send() {
        const input = document.getElementById("msg");
        socket.emit("message", { message: input.value });
        input.value = "";
      }
    </script>
  </body>
</html>

動作確認

localhost:5000/hoge に複数のブラウザでアクセスしてメッセージのやり取りができることを確認しましょう

できない場合はファイアウォールや NetFilter、リバースプロキシなどの設定を確認しましょう
WebSocket は Upgrade: example/1, foo/2 のような Upgrade ヘッダの許可が必須なので前段でヘッダが弾かれている可能性があります

最後に

5000 番ポートがすでに使われている場合はポートを変更するか AirPlay レシーバをオフにしましょう

lsof -i :5000

COMMAND    PID         USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ControlCe 1129       user01   10u  IPv4 0xfb24a12bbd08b2c2      0t0  TCP *:commplex-main (LISTEN)
ControlCe 1129       user01   11u  IPv6 0x9dc35b82883e083d      0t0  TCP *:commplex-main (LISTEN)

0 件のコメント:

コメントを投稿