发布于 2026-01-06 3 阅读
0

构建一个简单的网页聊天工具

构建一个简单的网页聊天工具

您好。在这篇文章中,您将学习如何使用 Python 和 WebSocket 构建一个简单的在线聊天系统。因此,您需要在计算机上安装Python 。

该项目的源代码可以在这个GitHub 仓库中找到,您也可以在Papo Reto网站上在线查看

如果您没有该flask软件包,可以使用以下命令进行安装:pip install Flask。您还需要flask_socketioeventlet,因此您可以运行pip install flask_socketiopip install eventlet来安装它们。

现在所有文件都已安装完毕,项目的文件和目录结构如下所示:

templates
 - index.html
static
   css
    - style.css
- main.py
Enter fullscreen mode Exit fullscreen mode

我们项目的核心是这个main.py文件,它将控制我们的应用程序。它看起来会像这样:

#! -*- enconding: utf-8 -*-
from flask import Flask, render_template
from flask_socketio import SocketIO, emit

app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static/')
socketio = SocketIO(app)

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

@socketio.on('client_message')
def receive_message (client_msg):
    emit('server_message', client_msg, broadcast=True)

if __name__ == '__main__':
    socketio.run(app)
Enter fullscreen mode Exit fullscreen mode

前两行代码导入了我们将在本项目中使用的包和模块。

from flask import Flask, render_template
from flask_socketio import SocketIO, emit
Enter fullscreen mode Exit fullscreen mode

Flask是我们的 Web 框架,是一个允许使用全双工低延迟通信协议(如 WebSocket)的flask_socketio软件包。flask

然后我们实例化 Flask 应用,并定义模板和静态文件的位置。我们还会实例化 Socket.IO 对象,并将 Flask 应用作为参数传递给它。

app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static/')
socketio = SocketIO(app)
Enter fullscreen mode Exit fullscreen mode

现在我们定义默认路由,它将返回index.html页面:

@app.route('/')
def index():
    return render_template('index.html')
Enter fullscreen mode Exit fullscreen mode

当用户发送消息时,服务器会收到 client_message 事件,并将昵称和消息作为参数传递给服务器。然后,服务器会将此消息广播给所有已连接的用户:

@socketio.on('client_message')
def receive_message (client_msg):
    emit('server_message', client_msg, broadcast=True)
Enter fullscreen mode Exit fullscreen mode

最后,我们将使用以下代码启动应用程序:

if __name__ == '__main__':
    socketio.run(app)
Enter fullscreen mode Exit fullscreen mode

好了,我们的main.py文件现在已经完成了,接下来我们将进入该index.html文件。在这个例子中,它将包含三个主要部分:文件开头message-box、文件结尾input-box和文件末尾nickname-box。该文件的内容如下:

<!DOCTYPE html>
<html lang="pt-BR">
<head>
<title>Papo Reto</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>

</head>
<body>
<script type="text/javascript" charset="utf-8">
const socket = io();
socket.on('server_message', (data) => {
    let e = document.createElement('p');
    let sp = document.createElement('span');
    sp.innerHTML = data.nickname;
    e.appendChild(sp);
    e.innerHTML = e.innerHTML+'>> '+data.message;
    if(document.getElementById('message-box').children.length>20){
        document.getElementById('message-box').removeChild(document.getElementById('message-box').children[0]);
    }
    document.getElementById('message-box').appendChild(e);

    document.getElementById('message-box').scroll(0, document.getElementById('message-box').scrollHeight);
});
function htmlEntities(str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function sendMessage(){
    socket.emit('client_message', {'nickname': htmlEntities(document.getElementById('nickname-input').value), 'message':  htmlEntities(document.getElementById('message-input').value)});
    document.getElementById('message-input').value = '';
}
function isPressingEnter(e){
    let k;
    if(window.event){
        k = e.keyCode;
        if(k===13){
            sendMessage();
        }
    }else if(e.which){
        k = e.which;
        if(k===13){
            sendMessage();
        }
    }
}
</script>

<h1 id="title">Papo Reto</h1>
<section id="chat-box">
    <section id="message-box">
    </section>

    <section id="input-box">
        <input type="text" autofocus onkeypress="return isPressingEnter(event)" required placeholder="Digite sua mensagem aqui" id="message-input">
        <button type="button" id="send-button" onclick="sendMessage()" >>></button> 
    </section>
</section>

<section id="nickname-box">
    <label id="nickname-label" for="nickname-input">Nickname: </label>
    <input type="text" id="nickname-input" autocomplete="off" value="Guest">
</section>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

我们使用SocketIO客户端 API 来连接和处理 WebSocket 连接。因此,在这种情况下,我们使用 CDN 来实现这一点:

<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>
Enter fullscreen mode Exit fullscreen mode

现在,我们需要使用 JavaScript 定义一个 socket 对象,该对象将连接到服务器:

const socket = io();
Enter fullscreen mode Exit fullscreen mode

由于 WebSocket 连接在同一路由上,因此我们不需要传递任何参数。

当用户点击发送按钮时,会调用该sendMessage函数。但如果用户在带有指定input-boxid 的输入框中输入内容,则会调用另一个isPressingEnter函数,该函数会验证用户是否按下了回车键。如果按下了回车键,还会调用sendMessage()

function isPressingEnter(e){
    let k;
    if(window.event){
        k = e.keyCode;
        if(k===13){
            sendMessage();
        }
    }else if(e.which){
        k = e.which;
        if(k===13){
            sendMessage();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

然后,该sendMessage函数会client_message向服务器发出一个事件,并将昵称和消息作为数据传递:

function sendMessage(){
    socket.emit('client_message', {'nickname': htmlEntities(document.getElementById('nickname-input').value), 'message':  htmlEntities(document.getElementById('message-input').value)});
    document.getElementById('message-input').value = '';
}
Enter fullscreen mode Exit fullscreen mode

我们还使用该htmlEntities函数从输入框中移除 HTML 标签:

function htmlEntities(str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
Enter fullscreen mode Exit fullscreen mode

当服务器收到消息时,它会通过发出server_message事件将消息广播给所有人。因此,我们需要将此消息添加到收到事件的message-box通知中:server_message

socket.on('server_message', (data) => {
    let e = document.createElement('p');
    let sp = document.createElement('span');
    sp.innerHTML = data.nickname;
    e.appendChild(sp);
    e.innerHTML = e.innerHTML+'>> '+data.message;
    if(document.getElementById('message-box').children.length>20){
        document.getElementById('message-box').removeChild(document.getElementById('message-box').children[0]);
    }
    document.getElementById('message-box').appendChild(e);

    document.getElementById('message-box').scroll(0, document.getElementById('message-box').scrollHeight);
});
Enter fullscreen mode Exit fullscreen mode

现在运行服务器python main.py,你会发现我们的在线聊天功能已经可以正常工作了。
WebChat 运行无需 CSS

但由于它看起来很糟糕,我们将添加一些 CSS 样式。Flask 模板使用jinja作为其模板引擎,因此将 CSS 文件路径添加到页面的方式略有不同index。我们可以通过在 标签内添加以下代码来实现head

<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='./css/stylxe.css') }}">
Enter fullscreen mode Exit fullscreen mode

您可以随意操作,但在这里,这是我们 CSS 文件的内容:

*{
    margin: 0;
    padding: 0;
}
body{
    background: #202020;
    color: #eee;
    font-family: 'monospace';
    line-height: 1.4em; 
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
    padding: 15px;
}
#title{
    font-size: calc(20px + 2vw + 1vh);
    padding: 40px;
}
#chat-box{
    border: 2px solid #ccc;
    border-radius: 10px;
    box-shadow: 3px 4px 4px #111;
}
@media only screen and (max-width: 600px){
    #chat-box{
        width: 95vw;
    }
}
@media only screen and (min-width: 600px){
    #chat-box{
        width: 80vw;
    }
}
#message-box{
    width: 100%;
    height: 60vh;
    background: #444;
    overflow-y: scroll;
    padding: 5px;
    font-size: calc(10px + 1vh + 2vw);
}
#message-box p{
    width: 100%;
    line-height: 1.5em;
    overflow-wrap: break-word;
    padding: 5px 2px;
    margin: 2px 0;
    background: #4f4f4f;
}
#message-box span{
    color: yellow;
    font-weight: 900;
}

#input-box{
    width: 100%;
    height: 100%;
    display: grid;
    grid-template-columns: 90% 10%;
}
#message-input{
    height: 90%;
    font-size: calc(10px + 1vh + 2vw);
    border-radius: 0 0 0 6px;
}
#send-button{
    background: purple;
    color: #f0f0a0;
    height: 100%;
    font-size: calc(10px + 1vh);
    font-weight: 900;
}
#nickname-box{
    padding: 15px;
}
#nickname-input {
    border-radius: 5px;
    background: purple;
    color: white;
    padding: 2px;
    font-size: calc(10px + 1vh + 1vw);
}
#nickname-label{
    font-size: calc(10px + 1vh + 1vw);

}
Enter fullscreen mode Exit fullscreen mode

这就是结果:
运行网络聊天

谢谢大家的关注!这是我们用 Python 构建一个非常简单的在线聊天工具的方法之一。而且,实现起来也很容易,但如果你们需要,我还可以演示如何将其部署到 Heroku 上,这样你们就可以让它在线运行了。

文章来源:https://dev.to/raymag/building-a-simple-webchat-2504