This repository was archived by the owner on Sep 5, 2026. It is now read-only.
Repository navigation
Expand file tree
/
Copy pathtest_tcp_basic.py
More file actions
293 lines (234 loc) · 8.94 KB
/
Copy pathtest_tcp_basic.py
File metadata and controls
293 lines (234 loc) · 8.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
"""
Basic TCP Server Test
Simplified test without complex dependencies.
"""
import asyncio
import struct
import socket
from enum import IntEnum
from dataclasses import dataclass
from typing import List
class PacketType(IntEnum):
HEARTBEAT = 0x01
HANDSHAKE = 0x02
SERVER_LIST = 0x03
CONNECT_REQUEST = 0x04
CONNECT_RESPONSE = 0x05
DISCONNECT = 0x06
ERROR = 0x07
AUTH = 0x08
@dataclass
class MockServerInfo:
id: str
name: str
host: str
port: int
class SimpleTCPServer:
"""Simplified TCP server for testing"""
def __init__(self, host="127.0.0.1", port=25566):
self.host = host
self.port = port
self.server = None
self.connections = []
self.servers = [
MockServerInfo("test-1", "Test Server 1", "192.168.1.1", 25565),
MockServerInfo("test-2", "Test Server 2", "192.168.1.2", 25565),
]
async def start(self):
self.server = await asyncio.start_server(
self._handle_client, self.host, self.port
)
print(f"[SERVER] Started on {self.host}:{self.port}")
async def stop(self):
if self.server:
self.server.close()
await self.server.wait_closed()
async def _handle_client(self, reader, writer):
addr = writer.get_extra_info('peername')
print(f"[SERVER] Client connected: {addr}")
self.connections.append(writer)
try:
while True:
# Read length
length_data = await reader.read(4)
if not length_data:
break
length = struct.unpack('>I', length_data)[0]
# Read type
type_byte = await reader.read(1)
packet_type = struct.unpack('B', type_byte)[0]
# Read payload
payload_length = length - 1
payload = await reader.read(payload_length) if payload_length > 0 else b''
await self._process_packet(writer, packet_type, payload)
except Exception as e:
print(f"[SERVER] Client error: {e}")
finally:
print(f"[SERVER] Client disconnected: {addr}")
self.connections.remove(writer)
writer.close()
async def _process_packet(self, writer, packet_type, payload):
if packet_type == PacketType.HANDSHAKE:
print(f"[SERVER] Received HANDSHAKE")
# Send server list
await self._send_server_list(writer)
elif packet_type == PacketType.HEARTBEAT:
print(f"[SERVER] Received HEARTBEAT")
# Echo heartbeat
await self._send_heartbeat(writer)
elif packet_type == PacketType.CONNECT_REQUEST:
print(f"[SERVER] Received CONNECT_REQUEST")
# Send connect response
await self._send_connect_response(writer, True)
async def _send_server_list(self, writer):
import io
buf = io.BytesIO()
# Write server count
buf.write(struct.pack('>I', len(self.servers)))
for srv in self.servers:
# Write server info
for s in [srv.id, srv.name, "", srv.host]:
data = s.encode('utf-8')
buf.write(struct.pack('>I', len(data)))
buf.write(data)
buf.write(struct.pack('>I', srv.port))
buf.write(struct.pack('>I', 50)) # latency
buf.write(struct.pack('>I', 0)) # player count
buf.write(struct.pack('>I', 20)) # max players
version = "1.20.6"
data = version.encode('utf-8')
buf.write(struct.pack('>I', len(data)))
buf.write(data)
payload = buf.getvalue()
total_length = 1 + len(payload)
writer.write(struct.pack('>I', total_length))
writer.write(struct.pack('B', PacketType.SERVER_LIST))
writer.write(payload)
await writer.drain()
print(f"[SERVER] Sent SERVER_LIST ({len(self.servers)} servers)")
async def _send_heartbeat(self, writer):
writer.write(struct.pack('>I', 1))
writer.write(struct.pack('B', PacketType.HEARTBEAT))
await writer.drain()
async def _send_connect_response(self, writer, success):
import io
buf = io.BytesIO()
buf.write(struct.pack('B', 1 if success else 0))
for s in ["test-server", "127.0.0.1", "Connected"]:
data = s.encode('utf-8')
buf.write(struct.pack('>I', len(data)))
buf.write(data)
buf.write(struct.pack('>I', 30000)) # local port
payload = buf.getvalue()
total_length = 1 + len(payload)
writer.write(struct.pack('>I', total_length))
writer.write(struct.pack('B', PacketType.CONNECT_RESPONSE))
writer.write(payload)
await writer.drain()
print(f"[SERVER] Sent CONNECT_RESPONSE")
class MockClient:
"""Mock client for testing"""
def __init__(self):
self.reader = None
self.writer = None
self.packets = []
async def connect(self, host="127.0.0.1", port=25566):
try:
self.reader, self.writer = await asyncio.open_connection(host, port)
print(f"[CLIENT] Connected to {host}:{port}")
return True
except Exception as e:
print(f"[CLIENT] Connection failed: {e}")
return False
async def send_handshake(self):
import io
buf = io.BytesIO()
buf.write(struct.pack('>I', 1)) # protocol version
buf.write(struct.pack('>I', 2)) # mod version
for s in ["1.20.6", "test-uuid", "TestPlayer"]:
data = s.encode('utf-8')
buf.write(struct.pack('>I', len(data)))
buf.write(data)
payload = buf.getvalue()
total_length = 1 + len(payload)
self.writer.write(struct.pack('>I', total_length))
self.writer.write(struct.pack('B', PacketType.HANDSHAKE))
self.writer.write(payload)
await self.writer.drain()
print("[CLIENT] Sent HANDSHAKE")
async def read_packets(self, timeout=5):
"""Read packets for a given time"""
end_time = asyncio.get_event_loop().time() + timeout
while asyncio.get_event_loop().time() < end_time:
try:
# Wait for data with timeout
await asyncio.wait_for(
self._read_one_packet(),
timeout=end_time - asyncio.get_event_loop().time()
)
except asyncio.TimeoutError:
break
async def _read_one_packet(self):
length_data = await self.reader.read(4)
if not length_data:
return
length = struct.unpack('>I', length_data)[0]
type_byte = await self.reader.read(1)
packet_type = struct.unpack('B', type_byte)[0]
payload_length = length - 1
payload = await self.reader.read(payload_length) if payload_length > 0 else b''
self.packets.append((packet_type, payload))
print(f"[CLIENT] Received: {PacketType(packet_type).name}")
def close(self):
if self.writer:
self.writer.close()
async def run_test():
"""Run basic TCP test"""
print("=" * 60)
print("Basic TCP Server Test")
print("=" * 60)
# Start server
server = SimpleTCPServer()
await server.start()
# Give server time to start
await asyncio.sleep(0.5)
# Connect client
client = MockClient()
connected = await client.connect()
if not connected:
print("[TEST] FAILED: Connection failed")
await server.stop()
return False
# Send handshake
await client.send_handshake()
# Read responses
await client.read_packets(timeout=3)
# Cleanup
client.close()
await server.stop()
# Results
print("\n" + "=" * 60)
print("Test Results")
print("=" * 60)
print(f"Connection: {'PASS' if connected else 'FAIL'}")
print(f"Packets received: {len(client.packets)}")
if client.packets:
print("\nReceived packets:")
for pkt_type, _ in client.packets:
print(f" - {PacketType(pkt_type).name}")
success = connected and len(client.packets) > 0
print(f"\nOverall: {'PASS' if success else 'FAIL'}")
return success
if __name__ == "__main__":
import sys
try:
result = asyncio.run(run_test())
sys.exit(0 if result else 1)
except KeyboardInterrupt:
print("\n[TEST] Interrupted")
sys.exit(1)
except Exception as e:
print(f"\n[TEST] Error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)