python - возникли проблемы с отправкой изображения через сокет - PullRequest
1 голос
/ 08 марта 2020

Я новичок в python и пытаюсь создать сценарий, который отправляет / передает скриншот моего сокета через ноутбук. Если я запускаю эти два сценария, это работает, но через несколько секунд (я думаю, что именно так и происходит: клиент перегружен информацией), он выдает мне ошибку «pickle data is truncated». Учебник, которому я следовал, был предназначен только для отправки строкового сообщения и использования буфера, но проблема в том, что я использую массив numpy, который обрабатывается, и если я его буферизую, он запишет второе изображение в первое и выдает еще одну усеченную ошибку, и я не знаю, что делать.

Любая помощь приветствуется.

сервер:

import numpy as np
import socket
import pickle
import time
import mss
import cv2
import sys

s = socket.socket()
shost = socket.gethostname()
host = socket.gethostbyname(shost) 
port = 8080
s.bind((host,port))
s.listen(5)
print(host)
print("Waiting for any incoming connections ... ")
conn, addr = s.accept()
print(addr, "Has connected to the server")

with mss.mss() as sct: #if i want it to be a different size
    monitor = {"top": 0, "left": 0, "width": 1720, "height": 720}


while True:
    img =  np.array(sct.grab(sct.monitors[1]))
    pack = pickle.dumps(img)
    conn.send(pack)
    print(sys.getsizeof(pack))

клиент:

import socket
import numpy as np
import cv2
import pickle
import time


s = socket.socket()
host = input(str("Please enter the host address of the sender : "))
port = 8080
s.connect((host,port))
print("Connected ... ")

while True:
    pack = s.recv(4196540)        
    frame = pickle.loads(pack)
    cv2.imshow("Screen", frame)
    pack = ' '
    if cv2.waitKey(1) == 27:
        break

cv2.destroyAllWindows()

1 Ответ

0 голосов
/ 10 марта 2020

socket.recv(), хотя выглядит похожим, не ведет себя так же, как .read(), если объект похож на файл. «Сырое» программирование сокетов также не выполняет большую часть служебной работы за вас, и ваш код должен справиться с ним.

Потоковый сокет (тип SOCK_STREAM, который открывается по умолчанию с использованием socket.socket() - это всего лишь поток байтов и, по сути, агности c контента, который вы проходите через него. Чтобы сделать вещи более сложными, как упомянуто Michael Butscher в комментариях (и здесь сходство с read() может сбивать с толку), его первый аргумент bufsize не говорит о том, сколько данных вы получите, он только ограничивает максимальный объем, который вы готовы получить сразу. Я получил буфер такого размера, дайте мне все, что вы можете до этого размера.

Если вы знаете, сколько данных вы хотите получить для любого изображения в вашем случае (и, поскольку они имеют фиксированный размер снимков экрана, я думаю, вам может повезти), вам нужно продолжать повторять вызовы recv() до тех пор, пока вы не получите полное изображение, затем выполнить свои действия с ним и перейти к следующему.

Это означает, что вы можете заменить:

pack = s.recv(4196540)

на что-то вроде:

item_size = 4196540
pack = b''
while len(pack) < item_size:
    remains = item_size - len(pack)
    bufsize = 4096 if remains > 4096 else remains
    pack += s.recv(bufsize)

Есть и другие способы справиться с этим, но по сути это работает recv до pack содержит 4196540 байтов данных (предположительно, одно изображение), а затем вы можете go включить для его обработки.

В противном случае вам придется использовать другой метод: вы можете, например, определить свой собственный протокол где сначала n байт - это размер файла, который следует, а затем вы знаете, сколько данных ожидать от следующего элемента. Или откройте новое соединение для каждого элемента (recv() на сокете, другая сторона которого была закрыта, не блокируется и выдает b'').

Кстати, последний комментарий также означает, что после отправки последнего изображения, сервер может сделать conn.close() и отключиться (или прослушать новые входящие соединения), и мы можем сделать еще одно изменение во фрагменте выше, а именно:

pack += s.recv(bufsize)

скажем:

buf = s.recv(bufsize)
if not buf:
    s.close()
    break  # end the "while True" loop
pack += buf

, чтобы также перестать получать на стороне клиента.

Также одна заметка относительно учебника, который вы упомянули. В тривиальных случаях ... например, при отправке коротких строковых сообщений, хотя вы не должны на это полагаться, вам часто везет и recv все ожидаемые данные в одном куске.

...