天天看點

基于 Python 的 Flask-SocketIO 架構 實作一個聊天系統

作者:richardd

Flask-SocketIO 是一個基于 Flask 和 Socket.IO 的 Python Web 應用程式開發庫。它使得在 Flask 應用中使用實時 Web 功能變得非常容易。

Socket.IO 是一個 JavaScript 庫,它允許雙向實時通信。它基于 WebSockets,但是也提供了向後相容的輪詢等技術,以支援所有 Web 浏覽器。

Flask-SocketIO 提供了許多有用的功能,例如:

  • 基于事件的消息通信,可以向特定的用戶端或所有用戶端廣播消息
  • 基于 Flask 視圖的 Socket.IO 端點
  • Flask-SocketIO 內建了 Werkzeug 的 HTTP 基礎設施,這使得使用 Flask-SocketIO 的應用程式具有很高的性能和可擴充性

下面是一個使用 Flask-SocketIO 的簡單示例:

pythonCopy codefrom flask import Flask
from flask_socketio import SocketIO, emit

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

@socketio.on('connect')
def handle_connect():
    print('client connected')

@socketio.on('message')
def handle_message(data):
    print('received message:', data)
    emit('response', 'hello from server')

if __name__ == '__main__':
    socketio.run(app)
           

在用戶端連接配接到伺服器時,會自動觸發 connect 事件。通過 on 方法注冊事件處理函數,可以在伺服器端處理用戶端發送的事件。使用 emit 方法可以向用戶端發送消息。

在用戶端使用 JavaScript 來處理事件,示例如下:

javascriptCopy codevar socket = io.connect('http://localhost:5000');
socket.on('connect', function() {
    console.log('connected to server');
});
socket.on('response', function(data) {
    console.log('received response:', data);
});
socket.emit('message', 'hello from client');
           

在用戶端連接配接到伺服器時,會自動觸發 connect 事件。使用 on 方法注冊事件處理函數,可以在用戶端處理伺服器發送的事件。使用 emit 方法可以向伺服器發送消息。

使用 Flask-SocketIO 可以輕松地實作實時 Web 功能。與 Flask-SocketIO 一起使用的 Socket.IO 還提供了許多其他功能,例如房間、命名空間等。

下面是一個簡單的聊天室示例,使用 Flask-SocketIO 實作。

server.py:

pythonCopy codefrom flask import Flask, render_template
from flask_socketio import SocketIO, emit

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

# 儲存線上使用者
online_users = set()

@app.route('/')
def index():
    return render_template('index.html')

# 接收用戶端發送的消息
@socketio.on('message')
def handle_message(data):
    emit('broadcast', data, broadcast=True)

# 使用者上線
@socketio.on('login')
def handle_login(username):
    # 将使用者添加到線上使用者集合
    online_users.add(username)
    emit('user_list', list(online_users), broadcast=True)
    emit('broadcast', f'{username} has joined the chat', broadcast=True)

# 使用者下線
@socketio.on('logout')
def handle_logout(username):
    # 将使用者從線上使用者集合中移除
    online_users.remove(username)
    emit('user_list', list(online_users), broadcast=True)
    emit('broadcast', f'{username} has left the chat', broadcast=True)

if __name__ == '__main__':
    socketio.run(app)
           

templates/index.html:

templates/index.html:

htmlCopy code<!doctype html>
<html>
  <head>
    <title>Chat Room</title>
    <script src="https://cdn.socket.io/4.4.1/socket.io.min.js" integrity="sha384-+7Vh13f86uA7bflvRj83M7hPG/E17FtwupV7q3Un19/0VfYQOxFh/7xNcE0RIs1" crossorigin="anonymous"></script>
    <script src="//code.jquery.com/jquery-3.6.0.min.js"></script>
  </head>
  <body>
    <h1>Chat Room</h1>
    <div id="message-list"></div>
    <div>
      <input type="text" id="username" placeholder="Your Name">
      <button id="login-btn">Login</button>
      <button id="logout-btn" disabled>Logout</button>
    </div>
    <div>
      <input type="text" id="message" placeholder="Your Message">
      <button id="send-btn" disabled>Send</button>
    </div>
    <div id="user-list"></div>
    <script>
      $(function() {
        var socket = io();
        var username = '';

        // 顯示線上使用者清單
        function showUserList(users) {
          var html = '<h3>Online Users:</h3><ul>';
          users.forEach(function(user) {
            html += '<li>' + user + '</li>';
          });
          html += '</ul>';
          $('#user-list').html(html);
        }

        // 顯示聊天記錄
        function showMessageList(message) {
          $('#message-list').append($('<p>').text(message));
        }

        // 登入按鈕事件處理函數
        $('#login-btn').click(function() {
          username = $('#username').val();
          socket.emit('login', username);
          $('#username').val('');
          $('#username').attr('disabled', 'disabled');
          $('#login-btn').attr('disabled', 'disabled');
          $('#logout-btn').removeAttr('disabled');
          $('#send-btn').removeAttr('disabled');
        });

        // 登出按鈕事件處理函數
        $('#logout-btn').click(function() {
          socket.emit('logout', username);
          $('#message-list'). empty();
          $('#username').removeAttr('disabled');
          $('#login-btn').removeAttr('disabled');
          $('#logout-btn').attr('disabled', 'disabled');
          $('#send-btn').attr('disabled', 'disabled');
          username = '';
        });

        // 發送消息按鈕事件處理函數
        $('#send-btn').click(function() {
          var message = $('#message').val();
          socket.emit('message', '[' + username + '] ' + message);
          $('#message').val('');
        });

        // 顯示線上使用者清單
        socket.on('user_list', function(users) {
          showUserList(users);
        });

        // 顯示聊天記錄
        socket.on('broadcast', function(message) {
          showMessageList(message);
        });
      });
    </script>
  </body>
</html>           

在終端中運作 python server.py 啟動服務端,然後在浏覽器中通路 http://localhost:5000 即可進入聊天室。

該示例中,通過 emit 方法向用戶端發送消息,通過 on 方法接收用戶端發送的消息。broadcast 參數表示将消息廣播給所有用戶端。

此外,還通過 online_users 集合儲存線上使用者清單,并在使用者登入、登出時更新使用者清單。