-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimpleXP_file_sender.py
More file actions
422 lines (350 loc) · 16.3 KB
/
simpleXP_file_sender.py
File metadata and controls
422 lines (350 loc) · 16.3 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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
import socket
import os
import sys
import shutil
import time
from datetime import datetime
import threading
# Version 2025-4-14_1455
# Configuration
PORT = 25565
CHUNK_SIZE = 8192 # Smaller chunks for better compatibility
SCAN_INTERVAL = 3 # Seconds between folder scans
def get_timestamp():
"""Get current time formatted as string"""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def print_with_timestamp(message):
"""Print a message with timestamp"""
timestamp = get_timestamp()
print("[%s] %s" % (timestamp, message))
def send_file(filepath, server_ip, port=PORT):
"""Send a single file to the server"""
try:
# Get file info
filename = os.path.basename(filepath)
filesize = os.path.getsize(filepath)
print_with_timestamp("Sending file: %s (%d bytes)" % (filename, filesize))
print_with_timestamp("Connecting to %s:%d..." % (server_ip, port))
# Create socket with timeout
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(30)
try:
# Connect to server
sock.connect((server_ip, port))
print_with_timestamp("Connected successfully")
# Send filename length (8 bytes, padded ASCII number)
name_bytes = filename.encode('utf-8')
name_length = str(len(name_bytes)).zfill(8).encode('ascii')
sock.sendall(name_length)
# Send filename
sock.sendall(name_bytes)
# Send file size (16 bytes, padded ASCII number)
size_bytes = str(filesize).zfill(16).encode('ascii')
sock.sendall(size_bytes)
# Send file data in chunks
bytes_sent = 0
with open(filepath, 'rb') as f:
start_time = time.time()
while bytes_sent < filesize:
# Read chunk
chunk = f.read(CHUNK_SIZE)
if not chunk:
break
# Send chunk
sock.sendall(chunk)
bytes_sent += len(chunk)
# Show progress occasionally
if bytes_sent % (CHUNK_SIZE * 10) == 0 or bytes_sent == filesize:
percent = int(bytes_sent * 100 / filesize)
print_with_timestamp("Progress: %d%% (%d/%d bytes)" % (percent, bytes_sent, filesize))
# Calculate speed
elapsed = time.time() - start_time
speed = filesize / (elapsed if elapsed > 0 else 1)
print_with_timestamp("File sent successfully! (%.1f KB/s)" % (speed/1024))
print_with_timestamp("Transfer complete")
# Create sent folder if it doesn't exist
base_dir = os.path.dirname(os.path.abspath(filepath))
sent_dir = os.path.join(base_dir, "sent")
if not os.path.exists(sent_dir):
os.makedirs(sent_dir)
# Move file to sent folder
sent_path = os.path.join(sent_dir, filename)
print_with_timestamp("Moving file to sent folder")
shutil.move(filepath, sent_path)
return True
except socket.error as e:
print_with_timestamp("Socket error: %s" % str(e))
return False
finally:
sock.close()
except Exception as e:
print_with_timestamp("Error: %s" % str(e))
return False
def watch_folder(server_ip, port=PORT):
"""Watch folder for files and send them"""
base_dir = os.path.dirname(os.path.abspath(__file__))
processed_files = set() # Track already processed files
try:
print("\n" + "="*50)
print("SIMPLE FILE SENDER")
print("="*50)
print("\nWatching for files to send to %s:%d" % (server_ip, port))
print("Place files in this folder to send them automatically")
print("Files will be moved to 'sent' folder after transfer")
print("Press Ctrl+C to stop\n")
# Create sent folder if needed
sent_dir = os.path.join(base_dir, "sent")
if not os.path.exists(sent_dir):
os.makedirs(sent_dir)
# Main loop
while True:
try:
# List files in current directory
files = []
try:
# More efficient way to scan directory
with os.scandir(base_dir) as entries:
for entry in entries:
if entry.is_file() and not entry.name in processed_files:
files.append(entry.name)
except AttributeError:
# Fallback for older Python versions
files = [f for f in os.listdir(base_dir)
if os.path.isfile(os.path.join(base_dir, f))
and not f in processed_files]
# Process new files
for filename in files:
filepath = os.path.join(base_dir, filename)
# Skip directories, this script, and system files
if (not filename.startswith('.') and
not filename.endswith('.exe') and
not filename.endswith('.pyc') and
not filename.endswith('.pyd') and
not filename.endswith('.dll') and
not filename.endswith('.bat') and
not filename.endswith('.log') and
filename != os.path.basename(__file__) and
not filename == "file_transfer_xp.py"):
# Send the file
success = send_file(filepath, server_ip, port)
# Add to processed files even if sending failed
# to avoid repeated attempts on problem files
processed_files.add(filename)
# Keep the processed files list manageable
if len(processed_files) > 1000:
processed_files = set(list(processed_files)[-500:])
# Wait before checking again - increase this value to reduce CPU usage
time.sleep(SCAN_INTERVAL)
except KeyboardInterrupt:
print_with_timestamp("Stopping file monitoring")
break
except Exception as e:
print_with_timestamp("Error in main loop: %s" % str(e))
def handle_client(client_socket, client_address, received_dir):
"""Handle incoming file transfer from a client"""
try:
print_with_timestamp("New connection from %s:%d" % client_address)
client_socket.settimeout(30)
# Receive filename length (8 bytes)
name_length_data = client_socket.recv(8)
if not name_length_data:
print_with_timestamp("Client disconnected - no filename length received")
return
try:
name_length = int(name_length_data.decode('ascii'))
print_with_timestamp("Filename length: %d bytes" % name_length)
except (ValueError, UnicodeDecodeError) as e:
print_with_timestamp("Error decoding filename length: %s" % str(e))
return
# Receive filename
filename_data = client_socket.recv(name_length)
if not filename_data:
print_with_timestamp("Client disconnected - no filename received")
return
try:
filename = filename_data.decode('utf-8')
print_with_timestamp("Filename: %s" % filename)
except UnicodeDecodeError as e:
print_with_timestamp("Error decoding filename: %s" % str(e))
return
# Receive file size (16 bytes)
size_data = client_socket.recv(16)
if not size_data:
print_with_timestamp("Client disconnected - no file size received")
return
try:
file_size = int(size_data.decode('ascii'))
print_with_timestamp("File size: %d bytes" % file_size)
except (ValueError, UnicodeDecodeError) as e:
print_with_timestamp("Error decoding file size: %s" % str(e))
return
# Prepare file path
filepath = os.path.join(received_dir, filename)
if os.path.exists(filepath):
base, ext = os.path.splitext(filename)
i = 1
while os.path.exists(os.path.join(received_dir, "%s_%d%s" % (base, i, ext))):
i += 1
filepath = os.path.join(received_dir, "%s_%d%s" % (base, i, ext))
print_with_timestamp("File already exists - saving as %s" % os.path.basename(filepath))
# Receive file data
received = 0
start_time = time.time()
with open(filepath, 'wb') as f:
while received < file_size:
# Calculate remaining bytes
remaining = file_size - received
# Read chunk (or remaining bytes if smaller)
chunk = client_socket.recv(min(CHUNK_SIZE, remaining))
if not chunk:
print_with_timestamp("Connection lost during transfer - got %d/%d bytes" %
(received, file_size))
break
# Write chunk and update progress
f.write(chunk)
received += len(chunk)
# Show progress occasionally
if received % (CHUNK_SIZE * 10) == 0 or received == file_size:
percent = int(received * 100 / file_size)
elapsed = time.time() - start_time
speed = received / (elapsed if elapsed > 0 else 1)
print_with_timestamp("Progress: %d%% (%d/%d bytes) - %.1f KB/s" %
(percent, received, file_size, speed/1024))
# Check if transfer was complete
if received == file_size:
elapsed = time.time() - start_time
speed = file_size / (elapsed if elapsed > 0 else 1)
print_with_timestamp("File received successfully: %s (%.1f KB/s)" %
(os.path.basename(filepath), speed/1024))
else:
print_with_timestamp("Incomplete file received: %s (%d of %d bytes)" %
(os.path.basename(filepath), received, file_size))
except Exception as e:
print_with_timestamp("Error handling client: %s" % str(e))
finally:
client_socket.close()
def receive_files(listen_ip=None, port=PORT):
"""Start server to receive files"""
try:
# Create received directory if needed
base_dir = os.path.dirname(os.path.abspath(__file__))
received_dir = os.path.join(base_dir, "received")
if not os.path.exists(received_dir):
os.makedirs(received_dir)
# Create server socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind to address
ip = listen_ip or '' # Empty string means listen on all interfaces
server.bind((ip, port))
server.listen(5)
server.settimeout(1) # Allow keyboard interrupt to work
print("\n" + "="*50)
print("FILE RECEIVER")
print("="*50)
print("\nListening for incoming files on %s:%d" % (ip or '*', port))
print("Received files will be saved to: %s" % received_dir)
print("Press Ctrl+C to stop\n")
# Accept connections until interrupted
while True:
try:
# Accept connection with timeout
client, addr = server.accept()
# Handle client in a separate thread
handler = threading.Thread(target=handle_client,
args=(client, addr, received_dir))
handler.daemon = True
handler.start()
except socket.timeout:
# This is expected - it allows the loop to check for keyboard interrupt
continue
except KeyboardInterrupt:
print_with_timestamp("Server stopping...")
break
except Exception as e:
print_with_timestamp("Error accepting connection: %s" % str(e))
time.sleep(1)
except Exception as e:
print_with_timestamp("Server error: %s" % str(e))
finally:
try:
server.close()
except:
pass
print_with_timestamp("Server stopped")
# Python 2 compatible input function that always returns a string
def get_input(prompt):
"""Get user input as string, compatible with both Python 2 and 3"""
if sys.version_info[0] >= 3:
return input(prompt)
else:
return raw_input(prompt)
# Main program entry point
if __name__ == "__main__":
try:
# Clear screen for better readability
os.system('cls' if os.name == 'nt' else 'clear')
print("\n" + "="*50)
print("SIMPLE FILE TRANSFER TOOL")
print("="*50)
print("\nWhat would you like to do?")
print("1. Send files")
print("2. Receive files")
choice = get_input("\nEnter your choice (1 or 2): ").strip()
if choice == "1":
# Sending files
# Get IP address from command line or via safe input
if len(sys.argv) > 1:
server_ip = sys.argv[1]
else:
# Use the safe input function
print("\nEnter the IP address of the receiver computer:")
server_ip = get_input("IP Address: ").strip()
# Parse IP and port if provided in format IP:PORT
port = PORT # Default port
if server_ip and ":" in server_ip:
try:
parts = server_ip.split(":")
ip_part = parts[0]
port_part = parts[1]
# Try to convert port to integer
port = int(port_part)
server_ip = ip_part
print("Using custom port: %d" % port)
except:
print("Invalid port format. Using default port: %d" % PORT)
# Start the file watcher if we have an IP
if server_ip:
watch_folder(server_ip, port)
else:
print("Error: IP address is required")
elif choice == "2":
# Receiving files
print("\nHow would you like to listen?")
print("1. Listen on all network interfaces (recommended)")
print("2. Listen on a specific IP address")
listen_choice = get_input("\nEnter your choice (1 or 2): ").strip()
listen_ip = None
if listen_choice == "2":
print("\nEnter the IP address to listen on:")
listen_ip = get_input("IP Address: ").strip()
# Get custom port if needed
use_custom_port = get_input("\nUse default port (%d)? (y/n): " % PORT).strip().lower()
if use_custom_port == 'n':
try:
port = int(get_input("Enter port number: ").strip())
except:
print("Invalid port number. Using default port: %d" % PORT)
port = PORT
else:
port = PORT
# Start the receiver
receive_files(listen_ip, port)
else:
print("Invalid choice.")
except KeyboardInterrupt:
print("\nProgram stopped by user")
except Exception as e:
print("\nError: %s" % str(e))
print("\nPress Enter to exit...")
get_input("")