Он не должен зависать, если обе стороны имеют одинаковую кодировку локали, но он может легко умереть за исключением.
Вы читаете и отправляете как двоичный файл (хорошо), но необъяснимым образом decode
-вернувшись к str
, затем encode
вернувшись к bytes
(плохо).Проблема в том, что произвольные двоичные данные не гарантируются для декодирования в любой заданной локали;если ваша кодировка локали UTF-8, скорее всего, это не законно.Если это latin-1
, это законно, но бессмысленно.
Хуже того, если ваш клиент и сервер имеют разные кодировки локали, результат декодирования может быть различным на каждой стороне (и, следовательно, длины не будут совпадать).
Используйте bytes
последовательно, не конвертируйте в и из строк, и настройки локали не будут иметь значения.Ваш код также будет работать быстрее.Вы также должны на самом деле отправить длину файла заранее;Ваш цикл надеется, что recv
вернет короткую длину только после завершения файла, но если:
- Файл является точным кратным размеру буфера, или
- случается, что сокет отправляет данные порциями, которые не соответствуют размеру буфера
, каждый из которых может получить короткие recv
результаты, по совпадению в случае № 2 и детерминистически в случае № 1.
Более безопасный подход заключается в том, чтобы на самом деле префикс вашей передачи иметь длину файла, а не надеяться, что порция работает как ожидалось:
def send_file(self,filename):
print("Sending:", filename)
with open(filename, 'rb') as f:
raw = f.read()
# Send actual length ahead of data, with fixed byteorder and size
self.sock.sendall(len(raw).to_bytes(8, 'big'))
# You have the whole thing in memory anyway; don't bother chunking
self.sock.sendall(raw)
def recv_file(self, conn, filename):
# Get the expected length (eight bytes long, always)
expected_size = b""
while len(expected_size) < 8:
more_size = conn.recv(8 - len(expected_size))
if not more_size:
raise Exception("Short file length received")
expected_size += more_size
# Convert to int, the expected file length
expected_size = int.from_bytes(expected_size, 'big')
# Until we've received the expected amount of data, keep receiving
packet = b"" # Use bytes, not str, to accumulate
while len(packet) < expected_size:
buffer = conn.recv(expected_size - len(packet))
if not buffer:
raise Exception("Incomplete file received")
packet += buffer
with open(filename, 'wb') as f:
f.write(packet)