120 lines
3.5 KiB
Python
120 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
||
"""最简 WebSocket 测试 - 验证基本通信"""
|
||
import socket, base64, os, struct, json, time, sys
|
||
|
||
IP = '198.120.0.100'
|
||
PORT = 8000
|
||
|
||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
sock.settimeout(10)
|
||
sock.connect((IP, PORT))
|
||
print(f"[✓] TCP 连接成功")
|
||
|
||
# WebSocket 握手
|
||
key = base64.b64encode(os.urandom(16)).decode()
|
||
req = (f"GET /ws HTTP/1.1\r\nHost: {IP}:{PORT}\r\nUpgrade: websocket\r\n"
|
||
f"Connection: Upgrade\r\nSec-WebSocket-Key: {key}\r\n"
|
||
f"Sec-WebSocket-Version: 13\r\n\r\n")
|
||
sock.sendall(req.encode())
|
||
|
||
# 接收 HTTP 响应
|
||
resp = b""
|
||
while b"\r\n\r\n" not in resp:
|
||
chunk = sock.recv(4096)
|
||
if not chunk: break
|
||
resp += chunk
|
||
print(f"[✓] HTTP 响应: {resp.split(b'\r\n')[0].decode()}")
|
||
|
||
# 检查响应码
|
||
if b"101" not in resp.split(b"\r\n")[0]:
|
||
print(f"[✗] 升级失败: {resp.decode(errors='replace')[:500]}")
|
||
sys.exit(1)
|
||
|
||
# 检查是否有额外的数据(WebSocket 帧紧跟在 HTTP 响应后)
|
||
headers_end = resp.find(b"\r\n\r\n") + 4
|
||
extra = resp[headers_end:]
|
||
if extra:
|
||
print(f"[i] HTTP 响应后有额外 {len(extra)} 字节: {extra.hex()}")
|
||
|
||
# 手动构造 WebSocket 文本帧发送 get_cmds
|
||
msg = json.dumps({"type": "get_cmds"})
|
||
payload = msg.encode()
|
||
L = len(payload)
|
||
mask = os.urandom(4)
|
||
masked = bytes(payload[i] ^ mask[i % 4] for i in range(L))
|
||
|
||
frame = bytearray([0x81]) # FIN + text
|
||
if L < 126:
|
||
frame.append(0x80 | L)
|
||
else:
|
||
frame.append(0x80 | 126)
|
||
frame.extend(struct.pack('>H', L))
|
||
frame.extend(mask)
|
||
frame.extend(masked)
|
||
sock.sendall(bytes(frame))
|
||
print(f"[→] 发送 get_cmds ({L} 字节)")
|
||
|
||
# 等待接收
|
||
sock.settimeout(3.0)
|
||
all_data = b""
|
||
try:
|
||
while True:
|
||
chunk = sock.recv(65536)
|
||
if not chunk:
|
||
break
|
||
all_data += chunk
|
||
print(f"[i] recv chunk: {len(chunk)} 字节")
|
||
except socket.timeout:
|
||
print(f"[i] recv 超时")
|
||
|
||
print(f"\n总共接收到: {len(all_data)} 字节")
|
||
|
||
if len(all_data) == 0:
|
||
print("[✗] 未收到任何数据!服务器可能未响应")
|
||
sys.exit(1)
|
||
|
||
# 解析 WebSocket 帧
|
||
offset = 0
|
||
while offset < len(all_data):
|
||
if offset + 2 > len(all_data): break
|
||
b0 = all_data[offset]
|
||
b1 = all_data[offset + 1]
|
||
opcode = b0 & 0x0F
|
||
length = b1 & 0x7F
|
||
offset += 2
|
||
if length == 126:
|
||
if offset + 2 > len(all_data): break
|
||
length = struct.unpack('>H', all_data[offset:offset+2])[0]
|
||
offset += 2
|
||
elif length == 127:
|
||
if offset + 8 > len(all_data): break
|
||
length = struct.unpack('>Q', all_data[offset:offset+8])[0]
|
||
offset += 8
|
||
|
||
if offset + length > len(all_data):
|
||
print(f" [W] 帧不完整: 需要 {length} 字节但只剩 {len(all_data)-offset}")
|
||
break
|
||
payload = all_data[offset:offset+length]
|
||
offset += length
|
||
|
||
if opcode == 0x01: # text
|
||
print(f" [text帧] {payload.decode('utf-8', errors='replace')[:500]}")
|
||
elif opcode == 0x02: # binary
|
||
try:
|
||
obj = json.loads(payload.decode('utf-8'))
|
||
print(f" [binary帧] JSON: type={obj.get('type','?')}, keys={list(obj.keys())}")
|
||
if obj.get('type') == 'cmd_list':
|
||
print(f" cmd_list: {len(obj.get('cmds',[]))} 个命令")
|
||
except:
|
||
print(f" [binary帧] {len(payload)} 字节: {payload[:200]}")
|
||
elif opcode == 0x08:
|
||
print(f" [close帧]")
|
||
elif opcode == 0x09:
|
||
print(f" [ping帧]")
|
||
elif opcode == 0x0A:
|
||
print(f" [pong帧]")
|
||
else:
|
||
print(f" [opcode={opcode}帧] {len(payload)} 字节")
|
||
|
||
sock.close()
|