python+flask_socketio+pyautogui实现简易远程桌面功能
python
import base64
import pyautogui
from flask import Flask
import time
from flask_socketio import SocketIO, emit
from io import BytesIO
import pyperclip
app=Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
@socketio.on('connect')
def handle_connect():
"""客户端连接时触发"""
print('客户端已连接')
emit('server_response', {'data': '连接成功'})
# 连接成功后,获取最新的远程屏幕
client_get_screen()
@socketio.on('client_send_keyboard_input')
def client_send_keyboard_input(json):
"""处理来自客户端的消息"""
print('收到客户端消息:', json)
# 模拟键盘输入
client_input=json['data']
print('客户端输入:', client_input)
# typewrite只会输入字母,其他字符会被忽略
pyautogui.typewrite(client_input)
client_get_screen()
@socketio.on('client_send_paste_key')
def client_send_paste_key(json):
"""处理来自客户端的粘贴消息"""
print('收到客户端粘贴消息:', json)
# 模拟粘贴操作
client_paste_text=json['data']
print('客户端粘贴文本:', client_paste_text)
# 先将文本复制到剪贴板
pyperclip.copy(client_paste_text)
# 模拟粘贴键
pyautogui.hotkey('ctrl', 'v')
client_get_screen()
@socketio.on('client_get_screen')
def client_get_screen():
"""处理获取远程屏幕请求"""
print('收到获取远程屏幕请求')
# 循环刷新3次
for i in range(5):
# 延迟0.2秒
time.sleep(0.2)
screen = pyautogui.screenshot()
# 将屏幕图片转换为base64编码
buffered = BytesIO()
screen.save(buffered, format="PNG")
screen_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
# 发送base64编码的屏幕图片给所有客户端
emit('remote_screen_update', {'data': screen_base64},broadcast=False)
@socketio.on('client_click_screen')
def handle_client_click(json):
"""处理来自客户端的点击事件"""
print('收到客户端点击事件:', json)
x, y = json['x'], json['y']
# 映射到主机屏幕坐标
screen_width, screen_height = pyautogui.size()
x, y = int(x * screen_width), int(y * screen_height)
pyautogui.moveTo(x, y)
pyautogui.click(x, y)
# emit('server_response', {'data': f'已点击屏幕位置: ({x}, {y})'},broadcast=False)
# 点击鼠标事件后,获取最新的远程屏幕
client_get_screen()
@socketio.on('client_dbclick_screen')
def handle_client_dbclick(json):
"""处理来自客户端的双击事件"""
print('收到客户端双击事件:', json)
x, y = json['x'], json['y']
# 映射到主机屏幕坐标
screen_width, screen_height = pyautogui.size()
x, y = int(x * screen_width), int(y * screen_height)
pyautogui.doubleClick(x, y)
# 双击鼠标事件后,获取最新的远程屏幕
client_get_screen()
@socketio.on('client_send_enter_key')
def handle_client_send_enter_key():
"""处理来自客户端的Enter键事件"""
print('收到客户端Enter键事件')
# 模拟键盘输入Enter键
pyautogui.press('enter')
client_get_screen()
@socketio.on('client_send_delete_key')
def handle_client_send_delete_key():
"""处理来自客户端的Delete键事件"""
print('收到客户端Delete键事件')
# 模拟键盘输入Delete键
pyautogui.press('backspace')
client_get_screen()
@app.route('/telescreen')
def index():
"""渲染主页"""
return '''
<!DOCTYPE html>
<html>
<head>
<meta lang="zh-CN">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>teledesk</title>
<style>
body{
background-color:black;
}
#telescreen {
position: relative;
background-color:skyblue;
box-shadow: 0 0 1em rgba(0, 0, 0, 0.5);
border:1em solid gray;
border-radius: 1em;
resize: both;
overflow: auto;
}
#remote_screen {
position: relative;
top: 0;
left: 0;
width: 100%;
}
#control_panel{
background-color: gray;
padding: 1em;
border-radius: 1em;
}
button,input
{
margin: 0.5em;
padding: 0.5em 1em;
border-radius: 0.5em;
border: 1px solid #666;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
// 连接到服务器
const socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
// 接收服务器消息
socket.on('server_response', (data) => {
console.log("收到服务器响应:", data);
});
// 接收远程屏幕
socket.on('remote_screen_update', (data) => {
console.log("收到远程屏幕:", data);
// 清除旧的远程屏幕图片
while (document.getElementById('telescreen').firstChild) {
document.getElementById('telescreen').removeChild(document.getElementById('telescreen').firstChild);
}
//创建新的远程屏幕图片
const remote_screen = document.createElement('img');
remote_screen.id = 'remote_screen';
remote_screen.src=` data:image/png;base64,${data.data}`;
remote_screen.alt = '远程屏幕';
remote_screen.addEventListener('click', (e) => {
const rect = e.target.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
console.log(`点击位置: (${x}, ${y})`);
socket.emit('client_click_screen', {x, y});
});
remote_screen.addEventListener('dbclick', (e) => {
const rect = e.target.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
console.log(`点击位置: (${x}, ${y})`);
socket.emit('client_dbclick_screen', {x, y});
});
document.getElementById('telescreen').appendChild(remote_screen);
});
// 绑定获取屏幕按钮事件
document.getElementById('get_screen').addEventListener('click', () => {
socket.emit('client_get_screen');
});
// 绑定发送按钮事件
document.getElementById('send_button').addEventListener('click', (e) => {
const input = document.getElementById('message_input');
socket.emit('client_send_keyboard_input', {data: input.value});
input.value = '';
});
// 绑定粘贴按钮事件
document.getElementById('paste_button').addEventListener('click', (e) => {
const input = document.getElementById('message_input');
socket.emit('client_send_paste_key', {data: input.value});
input.value = '';
});
// 绑定Enter键事件
document.getElementById('enter_button').addEventListener('click', (e) => {
socket.emit('client_send_enter_key');
});
// 绑定Delete键事件
document.getElementById('delete_button').addEventListener('click', (e) => {
socket.emit('client_send_delete_key');
});
});
</script>
</head>
<body>
<div id="telescreen">
<img id="remote_screen" src="" alt="远程屏幕">
</div>
<div id="control_panel">
<button id="get_screen">刷新远程屏幕</button>
<br/>
<input type="text" id="message_input" placeholder="输入要发送给远程屏幕的消息">
<br/>
<button id="send_button">发送输入文本</button>
<br/>
<button id="paste_button">粘贴输入文本</button>
<button id="delete_button">Delete</button>
<br/>
<button id="enter_button">Enter</button>
</div>
</body>
</html>
'''
if __name__ == '__main__':
socketio.run(app, debug=True,host='0.0.0.0',port=5000)