Open3

Online Messager実装

mabomabo

UDPを使ったシンプルなチャットシステムの骨格(ステップ1)

server.py
import socket
import time
from threading import Thread

class UDPServer:
    def __init__(self, host='localhost', port=12345):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.bind((host, port))
        self.clients = {}
        self.timeout = 30  # 30秒間非アクティブで切断
        
    def handle_client(self):
        while True:
            try:
                data, addr = self.sock.recvfrom(4096)
                if not data:
                    continue
                
                # メッセージ解析
                username_len = data[0]
                username = data[1:1+username_len].decode('utf-8')
                message = data[1+username_len:].decode('utf-8')
                
                # クライアント登録/更新
                self.clients[addr] = (username, time.time())
                
                # 全クライアントにリレー
                for client_addr in self.clients:
                    if client_addr != addr:
                        self.sock.sendto(data, client_addr)
                        
            except Exception as e:
                print(f"Error: {e}")
                
    def check_timeout(self):
        while True:
            current_time = time.time()
            to_delete = []
            for addr, (_, last_active) in self.clients.items():
                if current_time - last_active > self.timeout:
                    to_delete.append(addr)
            for addr in to_delete:
                del self.clients[addr]
            time.sleep(5)
            
    def start(self):
        print("Server started...")
        Thread(target=self.handle_client).start()
        Thread(target=self.check_timeout).start()

if __name__ == "__main__":
    server = UDPServer()
    server.start()
    while True:  # メインスレッドをブロック
        time.sleep(1)
client.py
import socket
import time
from threading import Thread

class UDPClient:
    def __init__(self, server_host='localhost', server_port=12345):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.server_addr = (server_host, server_port)
        self.username = input("Enter your username: ")
        
    def send_message(self, message):
        username_bytes = self.username.encode('utf-8')
        msg_bytes = message.encode('utf-8')
        data = bytes([len(username_bytes)]) + username_bytes + msg_bytes
        self.sock.sendto(data, self.server_addr)
        
    def receive_messages(self):
        while True:
            try:
                data, _ = self.sock.recvfrom(4096)
                username_len = data[0]
                username = data[1:1+username_len].decode('utf-8')
                message = data[1+username_len:].decode('utf-8')
                print(f"[{username}] {message}")
            except Exception as e:
                print(f"Error receiving: {e}")
                
    def start(self):
        Thread(target=self.receive_messages).start()
        while True:
            message = input()
            if message.lower() == 'exit':
                break
            self.send_message(message)
        self.sock.close()

if __name__ == "__main__":
    client = UDPClient()
    client.start()
mabomabo

機能要件・非機能要件

機能要件

システムやソフトウェアが「何をすべきか」を具体的に示したもの。
システムが提供する機能や動作に関する要求を指す。
システムが提供する具体的な機能や動作を定義します。
(例)メッセージの送受信、ユーザー名の入力など。

  1. サーバーの動作
    バックグラウンドでの稼働
    ・サーバーは常にバックグラウンドで動作し、クライアントからの接続を待ち受ける。
    オフライン時の挙動
    ・サーバーが停止している場合、チャットサービス全体が利用できなくなる。
  2. 通信プロトコル
    UDPソケットの使用
    ・サーバーとクライアントはUDP(User Datagram Protocol)を使ってメッセージをやり取りする。
    メッセージサイズの制限
    ・1回のメッセージ送信で最大4096バイトまで処理できます。
  3. ユーザー名の入力
    セッション開始時のユーザー名入力
    ・クライアントは接続時にユーザー名を入力する。
    ユーザー名のサイズ制限
    ・ユーザー名は最大255バイト(UTF-8エンコード)。
  4. メッセージのフォーマット
    メッセージの構造
    ・最初の1バイト:ユーザー名の長さ(usernamelen)。
    ・次のusernamelenバイト:ユーザー名。
    ・残りのバイト:実際のメッセージ。
    ・UTF-8エンコード:メッセージとユーザー名はUTF-8でエンコードされる。
  5. リレーシステム
    メッセージのリレー
    サーバーは受信したメッセージを、接続中のすべてのクライアントに転送する。
    クライアントの管理
    ・サーバーは接続中のクライアントの情報をメモリ上に保持する。
    タイムアウト処理
    ・一定時間メッセージを送信しないクライアントは自動的に切断される。

非機能要件

システムが「どのように動作すべきか」を定義するもの。
具体的には、パフォーマンス、信頼性、セキュリティなど、システムの品質に関する要求を指す。
システムの品質や性能に関する要求を定義する。
(例)リアルタイム性、パフォーマンス、信頼性など。

  1. リアルタイム性
    リアルタイム通信の優先
    ・チャットシステムでは、データのリアルタイム性が信頼性よりも優先される。つまり、メッセージが即座に届くことが重要。
  2. パフォーマンス
    高スループット
    ・システムは毎秒最低10,000パケットを処理できる必要がある。
    (例)1,000人のユーザーが同時にチャットしている場合、1秒間に10メッセージを処理できること。
    スケーラビリティ
    ・一般的なハードウェアで動作するように設計されている必要がある。
  3. 信頼性
    UDPの特性
    ・UDPはコネクションレスなプロトコルであり、パケットの到達保証や順序保証がない。そのため、アプリケーションレベルでの信頼性確保が必要。
    (例)タイムアウト処理や再送メカニズムの実装。
  4. セキュリティ
    クライアントの自動切断
    ・一定時間メッセージを送信しないクライアントは自動的に切断される。
    ユーザー情報の保護
    ・ユーザー名やメッセージが外部に漏れないようにする必要がある。
  5. ユーザビリティ
    シンプルなCLIインターフェース
    ・クライアントはコマンドラインインターフェース(CLI)を通じてサーバーに接続し、メッセージを送受信する。
mabomabo

実装時の注意点

  1. Pythonのバージョン:
    UbuntuとMacOSでデフォルトのPythonバージョンが異なる場合があります。python3コマンドを使って、明示的にPython 3.xを使用するようにしましょう。

  2. 仮想環境の使用:
    プロジェクトごとに仮想環境を作成し、依存ライブラリを管理することをお勧めします。

bash
python3 -m venv venv
source venv/bin/activate  # Ubuntu/MacOS共通
  1. ネットワーク設定:
    UDPソケットを使う場合、OSのファイアウォール設定でポートがブロックされないように注意してください。
    例: UbuntuやMacOSで特定のポートを開放する方法を確認しておきましょう。

  2. デバッグ:
    VS Codeのデバッグ機能はUbuntuでもMacOSでも同じように使えます。ブレークポイントを設定して、プログラムの動作を確認しましょう。