Вам, очевидно, повезло, когда вы запустили это по локальной сети. Ваш код неправильно отправляет поток изображений от отправителя получателю, потому что потоковые сокеты, такие как TCP, немного сложнее в использовании по своей природе. Основная проблема заключается в том, что ваш отправитель не сообщает, где заканчивается каждое изображение и начинается следующее, а ваш получатель аналогичным образом не организует данные, которые он читает, в отдельные полные изображения.
скажем, socket.sendall()
не сообщает конец своих данных получателю;Вы должны включить эту информацию в фактические данные, которые вы отправляете.
Обработка ошибок
Но прежде, чем это исправить, вы должны исправить свою обработку ошибок на получателе, чтобы вы получили больше полезных сообщений об ошибках,Когда вы пишете
except:
print("Something is broken...")
Вы выбрасываете что-то, что помогло бы вам больше, например, «EOFError: Out out of input» или «_pickle.UnpicklingError». Не выбрасывайте эту информацию. Вместо этого выведите его:
except:
traceback.print_exc()
или повторно поднимите его:
except Exception as err:
# do whatever you want to do first
raise err
или, поскольку вы хотите, чтобы он вызывал сбой вашей программы, и просто хотите сначала выполнить очистку, выполнитеВаша очистка в предложении finally
, нет необходимости в except
:
try:
# your code
finally:
# the cleanup
Потоковые сокеты и отправитель
Возвращаясь к коду вашего сокета, вы используете потоковые сокеты. Они посылают поток байтов, и, хотя вы можете рассчитывать на их поступление в правильном порядке, вы не можете рассчитывать, когда они прибудут. Если вы отправите b"something"
, а затем b"something else"
, вы можете получить b"somethingsomething else"
все сразу, b"somet"
, а затем b"hing"
и т. Д. Ваш получатель должен знать, где находится разделительная линия между каждым сообщением, поэтому шагкаждый делает там be разделительные линии между сообщениями. Есть несколько способов сделать это:
- Сделать все сообщения одинакового размера. Поскольку вы кодируете их как JPEG-файлы, которые могут иметь разные размеры в зависимости от того, как они сжаты, это будет немного сложнее и, возможно, совсем не то, что вам нужно. 1038 * или
b"\n\r"
. Сложнее заставить работать в вашей ситуации. - Отправка размера каждого сообщения перед отправкой. Это должно быть самым простым для вашего случая.
Конечно, если вы сейчас отправляете размер сообщения, это похоже на другое сообщение, и ваш получатель должен знать, где заканчивается это сообщение размера. Еще раз вы можете завершить сообщение о размере новой строкой:
s.sendall("{}\n".format(len(encoded)).encode("ascii"))
Или вы можете упаковать его в фиксированное число байтов, например 4:
s.sendall(struct.pack("!i", len(encoded)))
Получатель
Код получателя теперь должен читать полные сообщения, несмотря на то, что socket.recv()
может возвращать частичные сообщения или части нескольких сообщений вместе. Вы можете сохранить буфер входящих данных. Добавьте в конец, а затем удалите полные сообщения спереди:
buf = ''
while boo:
new_data = s.recv(4096)
if not new_data:
# exit, because the socket has been closed
buf += new_data
# if there's a full message at the beginning of buf:
# remove that message, but leave the rest in buf
# process that message
# else:
# nothing, just go back to receiving more
Конечно, чтобы найти ваше полное сообщение, сначала вам нужно получить сообщение в полном размере. Если вы закодировали все свои сообщения размером в 4 байта с struct.pack
, просто получите данные до тех пор, пока buf
не станет длиной 4 или более байтов, затем разделите их на размер и оставшиеся данные:
message_size = struct.unpack("!i", buf[:4])[0]
buf = buf[4:]
Затемсделать то же самое с сообщением изображения. Получайте данные до тех пор, пока у вас не будет хотя бы message_size
байтов данных, разделите ваш буфер на первое сообщение изображения, которое вы можете декодировать и отобразить, и оставьте остаток в буфере.
Безопасность
Документация для pickle
гласит:
Предупреждение: Модуль pickle
небезопасен . Только распакуйте данные, которым вы доверяете. Можно создать данные злонамеренного рассола, которые будут выполнять произвольный код во время расслоения . Никогда не извлекайте данные, которые могли прийти из ненадежного источника или могли быть подделаны.
В вашем случае, теоретически кто-то другой может подключиться к вашему IP на выбранном вами порту и отправить все, что он хочет, вашему получателю. Если это всего лишь игрушечный проект, который не будет запущен все время, шансы низкие.