Spaces:
Runtime error
Runtime error
Delete remote_client.py
Browse files- remote_client.py +0 -258
remote_client.py
DELETED
@@ -1,258 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
WebSocket远程客户端 - 在inferless环境中运行
|
4 |
-
用于连接到主服务器并执行远程命令和文件传输
|
5 |
-
"""
|
6 |
-
|
7 |
-
import asyncio
|
8 |
-
import websockets
|
9 |
-
import subprocess
|
10 |
-
import json
|
11 |
-
import logging
|
12 |
-
import sys
|
13 |
-
import urllib.parse
|
14 |
-
import base64
|
15 |
-
import os
|
16 |
-
import threading
|
17 |
-
import time
|
18 |
-
from pathlib import Path
|
19 |
-
|
20 |
-
# 配置日志
|
21 |
-
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
22 |
-
logger = logging.getLogger(__name__)
|
23 |
-
|
24 |
-
class RemoteClient:
|
25 |
-
def __init__(self, server_url, user_token, space_id):
|
26 |
-
self.server_url = server_url
|
27 |
-
self.user_token = user_token
|
28 |
-
self.space_id = space_id
|
29 |
-
self.websocket = None
|
30 |
-
self.is_connected = False
|
31 |
-
|
32 |
-
async def connect(self):
|
33 |
-
"""连接到WebSocket服务器"""
|
34 |
-
# 构建WebSocket URL
|
35 |
-
ws_url = self.server_url.replace('http', 'ws').replace('https', 'wss')
|
36 |
-
uri = f"{ws_url}/ws/client/{self.space_id}?token={urllib.parse.quote(self.user_token)}"
|
37 |
-
|
38 |
-
logger.info(f"正在连接到服务器: {uri}")
|
39 |
-
|
40 |
-
try:
|
41 |
-
async with websockets.connect(uri, ping_interval=20, ping_timeout=60) as websocket:
|
42 |
-
self.websocket = websocket
|
43 |
-
self.is_connected = True
|
44 |
-
logger.info("✅ 已连接到WebSocket服务器")
|
45 |
-
|
46 |
-
# 发送客户端注册信息
|
47 |
-
await self.register_client()
|
48 |
-
|
49 |
-
# 启动消息监听循环
|
50 |
-
await self.message_loop()
|
51 |
-
|
52 |
-
except Exception as e:
|
53 |
-
logger.error(f"❌ 连接失败: {e}")
|
54 |
-
self.is_connected = False
|
55 |
-
|
56 |
-
async def register_client(self):
|
57 |
-
"""注册客户端到服务器"""
|
58 |
-
registration = {
|
59 |
-
"type": "register",
|
60 |
-
"space_id": self.space_id,
|
61 |
-
"client_info": {
|
62 |
-
"environment": "inferless",
|
63 |
-
"python_version": sys.version,
|
64 |
-
"working_directory": os.getcwd()
|
65 |
-
}
|
66 |
-
}
|
67 |
-
await self.websocket.send(json.dumps(registration))
|
68 |
-
logger.info("📝 已发送客户端注册信息")
|
69 |
-
|
70 |
-
async def message_loop(self):
|
71 |
-
"""主消息处理循环"""
|
72 |
-
while self.is_connected:
|
73 |
-
try:
|
74 |
-
message = await self.websocket.recv()
|
75 |
-
data = json.loads(message)
|
76 |
-
await self.handle_message(data)
|
77 |
-
|
78 |
-
except websockets.exceptions.ConnectionClosed:
|
79 |
-
logger.error("🔌 WebSocket连接已关闭")
|
80 |
-
self.is_connected = False
|
81 |
-
break
|
82 |
-
except Exception as e:
|
83 |
-
logger.error(f"❌ 处理消息时出错: {e}")
|
84 |
-
|
85 |
-
async def handle_message(self, data):
|
86 |
-
"""处理收到的消息"""
|
87 |
-
message_type = data.get("type")
|
88 |
-
|
89 |
-
if message_type == "command":
|
90 |
-
await self.execute_command(data)
|
91 |
-
elif message_type == "upload_files":
|
92 |
-
await self.upload_files(data)
|
93 |
-
elif message_type == "ping":
|
94 |
-
await self.send_pong()
|
95 |
-
else:
|
96 |
-
logger.warning(f"⚠️ 未知消息类型: {message_type}")
|
97 |
-
|
98 |
-
async def execute_command(self, data):
|
99 |
-
"""执行远程命令"""
|
100 |
-
command = data.get("command", "")
|
101 |
-
logger.info(f"🚀 执行命令: {command}")
|
102 |
-
|
103 |
-
try:
|
104 |
-
# 执行命令
|
105 |
-
process = subprocess.run(
|
106 |
-
command,
|
107 |
-
shell=True,
|
108 |
-
capture_output=True,
|
109 |
-
text=True,
|
110 |
-
timeout=300 # 5分钟超时
|
111 |
-
)
|
112 |
-
|
113 |
-
# 发送执行结果
|
114 |
-
result = {
|
115 |
-
"type": "command_result",
|
116 |
-
"command": command,
|
117 |
-
"returncode": process.returncode,
|
118 |
-
"stdout": process.stdout,
|
119 |
-
"stderr": process.stderr,
|
120 |
-
"success": process.returncode == 0
|
121 |
-
}
|
122 |
-
|
123 |
-
await self.websocket.send(json.dumps(result))
|
124 |
-
|
125 |
-
if process.returncode == 0:
|
126 |
-
logger.info("✅ 命令执行成功")
|
127 |
-
else:
|
128 |
-
logger.error(f"❌ 命令执行失败,返回码: {process.returncode}")
|
129 |
-
|
130 |
-
except subprocess.TimeoutExpired:
|
131 |
-
error_result = {
|
132 |
-
"type": "command_result",
|
133 |
-
"command": command,
|
134 |
-
"error": "命令执行超时",
|
135 |
-
"success": False
|
136 |
-
}
|
137 |
-
await self.websocket.send(json.dumps(error_result))
|
138 |
-
logger.error("⏰ 命令执行超时")
|
139 |
-
|
140 |
-
except Exception as e:
|
141 |
-
error_result = {
|
142 |
-
"type": "command_result",
|
143 |
-
"command": command,
|
144 |
-
"error": str(e),
|
145 |
-
"success": False
|
146 |
-
}
|
147 |
-
await self.websocket.send(json.dumps(error_result))
|
148 |
-
logger.error(f"❌ 命令执行异常: {e}")
|
149 |
-
|
150 |
-
async def upload_files(self, data):
|
151 |
-
"""上传指定目录中的所有文件"""
|
152 |
-
upload_dir = data.get("directory", "output")
|
153 |
-
|
154 |
-
logger.info(f"📁 开始上传目录: {upload_dir}")
|
155 |
-
|
156 |
-
if not os.path.exists(upload_dir):
|
157 |
-
error_msg = f"目录不存在: {upload_dir}"
|
158 |
-
logger.error(error_msg)
|
159 |
-
await self.send_upload_result(False, error_msg)
|
160 |
-
return
|
161 |
-
|
162 |
-
try:
|
163 |
-
# 扫描目录中的所有文件
|
164 |
-
files_to_upload = []
|
165 |
-
for root, dirs, files in os.walk(upload_dir):
|
166 |
-
for file in files:
|
167 |
-
file_path = os.path.join(root, file)
|
168 |
-
if os.path.isfile(file_path):
|
169 |
-
files_to_upload.append(file_path)
|
170 |
-
|
171 |
-
if not files_to_upload:
|
172 |
-
msg = f"目录 {upload_dir} 中没有找到文件"
|
173 |
-
logger.info(msg)
|
174 |
-
await self.send_upload_result(True, msg)
|
175 |
-
return
|
176 |
-
|
177 |
-
logger.info(f"📦 找到 {len(files_to_upload)} 个文件,开始上传...")
|
178 |
-
|
179 |
-
success_count = 0
|
180 |
-
failed_count = 0
|
181 |
-
|
182 |
-
for file_path in files_to_upload:
|
183 |
-
try:
|
184 |
-
await self.upload_single_file(file_path)
|
185 |
-
success_count += 1
|
186 |
-
logger.info(f"✅ 上传成功: {os.path.basename(file_path)}")
|
187 |
-
except Exception as e:
|
188 |
-
failed_count += 1
|
189 |
-
logger.error(f"❌ 上传失败: {file_path} - {e}")
|
190 |
-
|
191 |
-
# 稍作延迟避免服务器压力
|
192 |
-
await asyncio.sleep(0.1)
|
193 |
-
|
194 |
-
result_msg = f"上传完成! 成功: {success_count}, 失败: {failed_count}"
|
195 |
-
logger.info(f"📊 {result_msg}")
|
196 |
-
await self.send_upload_result(True, result_msg)
|
197 |
-
|
198 |
-
except Exception as e:
|
199 |
-
error_msg = f"上传过程中出错: {e}"
|
200 |
-
logger.error(error_msg)
|
201 |
-
await self.send_upload_result(False, error_msg)
|
202 |
-
|
203 |
-
async def upload_single_file(self, file_path):
|
204 |
-
"""上传单个文件"""
|
205 |
-
filename = os.path.basename(file_path)
|
206 |
-
|
207 |
-
with open(file_path, 'rb') as f:
|
208 |
-
file_content = f.read()
|
209 |
-
file_b64 = base64.b64encode(file_content).decode('utf-8')
|
210 |
-
|
211 |
-
file_message = {
|
212 |
-
"type": "file_upload",
|
213 |
-
"filename": filename,
|
214 |
-
"content": file_b64,
|
215 |
-
"file_size": len(file_content)
|
216 |
-
}
|
217 |
-
|
218 |
-
await self.websocket.send(json.dumps(file_message))
|
219 |
-
|
220 |
-
async def send_upload_result(self, success, message):
|
221 |
-
"""发送上传结果"""
|
222 |
-
result = {
|
223 |
-
"type": "upload_result",
|
224 |
-
"success": success,
|
225 |
-
"message": message
|
226 |
-
}
|
227 |
-
await self.websocket.send(json.dumps(result))
|
228 |
-
|
229 |
-
async def send_pong(self):
|
230 |
-
"""响应ping消息"""
|
231 |
-
pong = {"type": "pong"}
|
232 |
-
await self.websocket.send(json.dumps(pong))
|
233 |
-
|
234 |
-
async def main():
|
235 |
-
if len(sys.argv) < 4:
|
236 |
-
print("使用方法: python remote_client.py <server_url> <user_token> <space_id>")
|
237 |
-
print("示例: python remote_client.py https://gkbtyo-rqvays-5001.preview.cloudstudio.work abc123 space456")
|
238 |
-
sys.exit(1)
|
239 |
-
|
240 |
-
server_url = sys.argv[1]
|
241 |
-
user_token = sys.argv[2]
|
242 |
-
space_id = sys.argv[3]
|
243 |
-
|
244 |
-
client = RemoteClient(server_url, user_token, space_id)
|
245 |
-
|
246 |
-
while True:
|
247 |
-
try:
|
248 |
-
await client.connect()
|
249 |
-
except KeyboardInterrupt:
|
250 |
-
logger.info("👋 收到中断信号,正在退出...")
|
251 |
-
break
|
252 |
-
except Exception as e:
|
253 |
-
logger.error(f"❌ 连接异常: {e}")
|
254 |
-
logger.info("🔄 5秒后重新尝试连接...")
|
255 |
-
await asyncio.sleep(5)
|
256 |
-
|
257 |
-
if __name__ == "__main__":
|
258 |
-
asyncio.run(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|