装好python环境直接就用 运行pyhton3 ./client.py 访问地址xxxx:5001
python代码
提示:这里可以添加技术概要
# -*- coding: utf-8 -*-
import socket
import threading
import json
from flask import Flask, render_template, request, redirect, url_for, session
from flask_socketio import SocketIO, emit
from gevent import monkey
# 修复阻塞问题
monkey.patch_all()
# 初始化 Flask 和 SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode="gevent", cors_allowed_origins="*")
# 用于存储客户端发送的消息
messages = []
# 用户登录信息(账号:密码)
user_credentials = {
"xxx": "123456",
"xxx1": "123456"
}
# 聊天页面路由
@app.route('/')
def login_page():
"""登录页面"""
return render_template('login.html')
@app.route('/login', methods=['POST'])
def login():
"""处理用户登录"""
username = request.form.get('username')
password = request.form.get('password')
# 校验用户名和密码
if username in user_credentials and user_credentials[username] == password:
session['username'] = username # 将用户名存入会话
return redirect(url_for('chat_page')) # 跳转到聊天页面
else:
return "登录失败,用户名或密码错误!<a href='/'>返回</a>"
@app.route('/chat')
def chat_page():
"""聊天页面"""
if 'username' in session:
return render_template('chat.html', username=session['username'])
else:
return redirect(url_for('login_page'))
@app.route('/logout')
def logout():
"""登出用户"""
session.pop('username', None)
return redirect(url_for('login_page'))
# WebSocket 事件监听
@socketio.on('connect')
def on_connect():
"""处理用户连接事件"""
username = session.get('username')
if username:
print(f"用户 {username} 已连接到 Web 页面")
# 将历史消息发送给登录用户
emit('chat_history', messages)
else:
print("未登录用户尝试连接")
# 断开未登录用户的连接
emit('unauthorized', {'message': '请先登录!'})
return False # 断开连接
@socketio.on('send_message')
def handle_send_message(data):
"""处理用户发送的消息"""
username = session.get('username')
if username:
message = data.get('message', '')
if message:
print(f"收到消息: {message} 来自用户: {username}")
# 保存消息
msg_data = {'username': username, 'message': message}
messages.append(msg_data) # 将消息存储到历史记录
# 广播消息到所有用户
socketio.emit('new_message', msg_data)
else:
emit('unauthorized', {'message': '未登录用户不能发送消息!'})
# 广播消息到页面
def broadcast_message(msg, ip_port):
"""广播 TCP 服务端接收到的消息"""
data = f"{ip_port}: {msg}"
messages.append(data)
socketio.emit('new_message', {'message': data})
# 处理客户端的请求操作
def handle_client_request(service_client_socket, ip_port):
"""处理 TCP 客户端的请求"""
try:
while True:
recv_data = service_client_socket.recv(1024)
if recv_data:
# 检测是否为 HTTP CONNECT 请求
if recv_data.startswith(b"CONNECT") or b"HTTP" in recv_data:
print(f"检测到 HTTP CONNECT 请求,来自 {ip_port}")
service_client_socket.send("不支持的请求类型".encode("utf-8"))
service_client_socket.close()
return
# 尝试解码数据
try:
message = recv_data.decode("utf-8")
print(f"收到消息: {message} 来自 {ip_port}")
# 如果数据是 JSON 格式,可以尝试加载
try:
data = json.loads(message)
broadcast_message(data.get('message', '未知消息'), ip_port)
except json.JSONDecodeError:
# 如果不是 JSON 格式,直接广播
broadcast_message(message, ip_port)
# 回复客户端
service_client_socket.send("消息已接收并显示在页面上".encode("utf-8"))
except UnicodeDecodeError:
print(f"收到无法解码的数据,来自 {ip_port}: {recv_data}")
service_client_socket.send("无法解码的消息".encode("utf-8"))
else:
print("客户端下线:", ip_port)
break
except Exception as e:
print(f"处理客户端请求时发生错误,来自 {ip_port}: {e}")
finally:
service_client_socket.close()
# 启动 TCP 服务端线程
def start_tcp_server():
"""启动 TCP 服务端线程"""
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_socket.bind(("", 8081))
tcp_server_socket.listen(128)
print("TCP 服务端已启动,等待客户端连接...")
while True:
service_client_socket, ip_port = tcp_server_socket.accept()
print("客户端连接成功:", ip_port)
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
sub_thread.setDaemon(True)
sub_thread.start()
# 启动服务
if __name__ == '__main__':
# 启动 TCP 服务端线程
threading.Thread(target=start_tcp_server, daemon=True).start()
# 启动 Flask Web 服务
socketio.run(app, host='0.0.0.0', port=5001)
页面代码
chat.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>聊天页面</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.6.0/socket.io.js"></script>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f3f4f6;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
height: 100vh;
}
h1 {
margin-top: 20px;
color: #333;
}
#chat-box {
background-color: #ffffff;
border: 1px solid #ccc;
border-radius: 8px;
width: 80%;
max-width: 600px;
height: 400px;
overflow-y: auto;
padding: 15px;
box-sizing: border-box;
margin-top: 20px;
}
#chat-box p {
margin: 5px 0;
padding: 8px;
background-color: #f3f3f3;
border-radius: 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
#chat-form {
margin-top: 20px;
display: flex;
justify-content: center;
width: 80%;
max-width: 600px;
}
#message {
flex: 1;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
button {
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: 4px;
background-color: #4CAF50;
color: white;
margin-left: 10px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #45a049;
}
</style>
</head>
<body>
<h1>欢迎,{{ username }}</h1>
<div id="chat-box">
<!-- 消息内容将动态加载到这里 -->
</div>
<form id="chat-form">
<input type="text" id="message" placeholder="输入消息" required>
<button type="submit">发送</button>
</form>
<script>
const socket = io.connect();
// 加载历史消息
socket.on('chat_history', function(messages) {
const chatBox = document.getElementById('chat-box');
messages.forEach(msg => {
chatBox.innerHTML += `<p><strong>${msg.username}:</strong> ${msg.message}</p>`;
});
});
// 显示新消息
socket.on('new_message', function(data) {
const chatBox = document.getElementById('chat-box');
chatBox.innerHTML += `<p><strong>${data.username}:</strong> ${data.message}</p>`;
chatBox.scrollTop = chatBox.scrollHeight; // 滚动到底部
});
// 发送消息
document.getElementById('chat-form').addEventListener('submit', function(e) {
e.preventDefault();
const message = document.getElementById('message').value.trim();
if (message) {
socket.emit('send_message', { message });
document.getElementById('message').value = ''; // 清空输入框
}
});
</script>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>聊天页面</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f9;
margin: 0;
padding: 0;
}
#messages {
margin: 20px;
padding: 10px;
background: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
max-height: 60vh;
overflow-y: auto;
}
.message {
margin: 5px 0;
padding: 10px;
background: #f1f1f1;
border-radius: 5px;
}
.message .sender {
font-weight: bold;
}
.login, .send-message {
margin: 20px;
padding: 10px;
background: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.login input, .send-message input {
margin: 5px 0;
padding: 5px;
width: calc(100% - 12px);
}
.login button, .send-message button {
margin-top: 10px;
padding: 5px 10px;
}
.current-user {
text-align: center;
font-size: 18px;
margin-top: 10px;
font-weight: bold;
}
</style>
</head>
<body>
<h1 style="text-align: center;">聊天页面</h1>
<!-- 显示当前用户 -->
<div class="current-user" id="currentUser"></div>
<!-- 登录模块 -->
<div class="login">
<h3>登录</h3>
<input type="text" id="username" placeholder="请输入用户名" />
<button onclick="login()">登录</button>
</div>
<!-- 消息区域 -->
<div id="messages"></div>
<!-- 发送消息模块 -->
<div class="send-message" style="display: none;">
<h3>发送消息</h3>
<input type="text" id="messageInput" placeholder="请输入消息" />
<button onclick="sendMessage()">发送</button>
</div>
<script>
let username = '';
const socket = io('http://172.18.1.26:5001/');
// 显示当前用户
function updateCurrentUser() {
const currentUserDiv = document.getElementById('currentUser');
currentUserDiv.textContent = `当前用户:${username}`;
}
// 登录逻辑
function login() {
const usernameInput = document.getElementById('username');
username = usernameInput.value.trim();
if (username) {
alert(`欢迎 ${username} 登录`);
updateCurrentUser();
document.querySelector('.login').style.display = 'none';
document.querySelector('.send-message').style.display = 'block';
} else {
alert('请输入有效用户名');
}
}
// 发送消息逻辑
function sendMessage() {
const messageInput = document.getElementById('messageInput');
const message = messageInput.value.trim();
if (message && username) {
socket.emit('send_message', { username, message });
messageInput.value = '';
} else {
alert('请先输入消息或登录');
}
}
// 加载历史消息
socket.on('chat_history', function (messages) {
const messagesDiv = document.getElementById('messages');
messages.forEach((data) => {
const messageElement = document.createElement('div');
messageElement.className = 'message';
messageElement.innerHTML = `<span class="sender">${data.username}:</span> ${data.message}`;
messagesDiv.appendChild(messageElement);
});
messagesDiv.scrollTop = messagesDiv.scrollHeight;
});
// 接收新消息
socket.on('new_message1', function (data) {
const messagesDiv = document.getElementById('messages');
const messageElement = document.createElement('div');
messageElement.className = 'message';
messageElement.innerHTML = `<span class="sender">${data.username}:</span> ${data.message}`;
messagesDiv.appendChild(messageElement);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
});
</script>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f3f4f6;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.login-container {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
padding: 30px;
max-width: 400px;
width: 100%;
box-sizing: border-box;
text-align: center;
}
.login-container h1 {
margin-bottom: 20px;
color: #333;
}
.login-container input[type="text"],
.login-container input[type="password"] {
width: 100%;
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
box-sizing: border-box;
}
.login-container button {
width: 100%;
padding: 10px;
background-color: #4CAF50;
color: white;
font-size: 16px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.login-container button:hover {
background-color: #45a049;
}
.login-container a {
display: inline-block;
margin-top: 15px;
color: #007BFF;
text-decoration: none;
}
.login-container a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="login-container">
<h1>登录</h1>
<form action="/login" method="post">
<input type="text" name="username" placeholder="用户名" required>
<input type="password" name="password" placeholder="密码" required>
<button type="submit">登录</button>
</form>
<a href="#">忘记密码?</a>
</div>
</body>
</html>
小结
提示:这里可以添加总结
公司电脑不让登录微信或QQ,又不好拿着手机发消息的,自己买个云服务器带有公网ip的,把程序部署上去打开网页聊天去吧

1742

被折叠的 条评论
为什么被折叠?



