Python-сокеты и Opencv - ConnectionResetError: [WinError 10054] - PullRequest
1 голос
/ 10 ноября 2019

Редактировать: чтобы уточнить: он компилируется, он просто падает почти сразу после загрузки потока. Он подключается правильно.

Итак, я очень долго пытался завершить мой проект. Я пытаюсь отправить видео через сокеты, используя cv2. Он работает через LAN, а не через WAN. Я получаю следующую ошибку:

"ConnectionResetError: [WinError 10054] Существующее соединение было принудительно закрыто удаленным хостом"

Код для клиента (отправкавидео закончено):

import cv2
import numpy as np
import socket
import pickle

host = "<insert public ip of recipient>"
port = 7643

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # declares s object with two parameters
s.connect((host, port))  # connects to the host & port
cap = cv2.VideoCapture(1)
while cap.isOpened(): # while camera is being used
    ret, frame = cap.read()  # reads each frame from webcam
    if ret:
        encoded = pickle.dumps(cv2.imencode(".jpg", frame)[1]) # encoding each frame, instead of sending live video it is sending pictures one by one
        s.sendall(encoded)
    if cv2.waitKey(1) & 0xFF == ord("q"): # wait until key was pressed once and
        break
cap.release()
cv2.destroyAllWindows()

Код для получателя (получения видео):

import cv2
import socket
import pickle

host = "192.168.1.186"
port = 7643
boo = True

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # declares s object with two parameters
s.bind((host, port))  # tells my socket object to connect to this host & port "binds it to it"
s.listen(10)  # tells the socket how much data it will be receiving.

conn, addr = s.accept()
while boo:
    try:
        pictures = conn.recv(256000)  # creates a pictures variable that receives the pictures with a max amount of 128000 data it can receive
        decoded = pickle.loads(pictures)  # decodes the pictures
        frame = cv2.imdecode(decoded, cv2.IMREAD_COLOR)  # translates decoded into frames that we can see!
        cv2.imshow("unique", frame)
        if cv2.waitKey(1) & 0xFF == ord("q"):  # wait until q key was pressed once and
            break
    except:
        print("Something is broken...")
        boo = False
        cv2.destroyAllWindows()
        s.close()

1 Ответ

0 голосов
/ 10 ноября 2019

Вам, очевидно, повезло, когда вы запустили это по локальной сети. Ваш код неправильно отправляет поток изображений от отправителя получателю, потому что потоковые сокеты, такие как 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 на выбранном вами порту и отправить все, что он хочет, вашему получателю. Если это всего лишь игрушечный проект, который не будет запущен все время, шансы низкие.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...