编辑:澄清一下:它确实可以编译,流加载后几乎立即崩溃。 它确实连接正确。
所以,我一直在努力完成我的这个项目。我正在尝试使用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()
答案 0 :(得分:0)
当您通过LAN运行它时,您显然很幸运。您的代码没有正确地将图像流从发送者发送到接收者,因为流套接字(如TCP)就其性质而言使用起来有些复杂。主要问题是您的发件人没有在每个图像的结尾处和下一个图像的开始处进行通信,而您的收件人也没有将其读取的数据整理成单独的完整图像。
也就是说,socket.sendall()
不会将其数据的结尾传达给接收者;您需要将该信息包含在您发送的实际数据中。
但是在解决此问题之前,应先对收件人进行错误处理,以获取更多有用的错误消息。当你写
except:
print("Something is broken...")
您扔掉了一些对您有帮助的东西,例如“ EOFError:输入不足”或“ _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 分隔线。有几种方法可以做到这一点:
b"\n"
或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
当然,要查找完整的消息,首先需要获取完整大小的消息。如果您使用struct.pack
将所有大小消息编码为4个字节,则只需接收数据,直到buf
的长度为4个或更多字节,然后将其拆分为大小和所有剩余数据:
message_size = struct.unpack("!i", buf[:4])[0]
buf = buf[4:]
然后对图像消息执行相同的操作。接收数据,直到您至少拥有message_size
个字节的数据,将缓冲区拆分为第一条图像消息(可以解码和显示),然后将其余部分保留在缓冲区中。
警告:
pickle
模块不安全。仅释放您信任的数据。 可以构造恶意的腌制数据,这些数据会在腌制过程中执行任意代码。永远不要挑剔那些可能来自不可信来源或被篡改的数据。
在您的情况下,理论上其他人可以在您选择的端口上连接到您的IP,然后将他们想要的任何内容发送给接收者。如果这只是一个玩具项目而不会一直运行,那么赔率很低。