Receiving a full image from a TCP socket communication - python-3.x

I have a TCP communication where my client continuously sends images as a byte array to a server and receives a response back, my problem is that when the server receives the images they are not done being received even though I've added a flag to indicate the end of the image.
I'd like to know a better way to ensure that the image file is received completely before receiving a new one
EDIT: My new attempt:
Client.py
import numpy as np
import cv2
from PIL import Image
import base64
import socket
def main(data):
s = socket.socket()
s.connect(("127.0.0.1", 999))
decoded_data = base64.b64decode(data)
print("Sending...")
s.sendall(decoded_data)
s.shutdown(s.SHUTWR)
b_data = b''
while True:
txt_data = s.recv(2048)
if not txt_data: break
b_data += txt_data
print('response received from the server: ' + b_data.decode())
return b_data.decode()
Server.py
import socket
from PIL import Image
import io
import numpy as np
import cv2
import uuid
IP = '127.0.0.1'
PORT = 999
with socket.socket() as s:
s.bind((IP,PORT))
s.listen(1)
count = 0
print ('The server is ready')
while True:
con, addr = s.accept()
filename = str(uuid.uuid4())
count = count + 1
img_dir = 'C:/Users/my_user/stream_images/'
img_format = '.png'
with con:
img = b''
while True:
data = con.recv(2048)
if not data:
break
img += data
image_name = img_dir+'frame'+str(count)+img_format
pil_image = io.BytesIO(img)
img = np.array(Image.open(pil_image))
img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
cCode = str('Thank you for connecting')
con.sendall(cCode.encode())
print("called con.sendall")
cv2.imshow('frame', img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
Currently, I am now able to fully send the images and receive them properly at the server, the only problem is that I am no longer sending a response back after the image is received, so there is something wrong with how I am receiving the reply message at the client side.

As user207421 suggested you can shutdown the socket for writing after sending the image on the client-side while still being able to receive an confirmatory answer from the server. Another problem you're facing here is the blocking nature of cv2.waitKey, which essentially halts the server until the user presses q in the cv2 window (the server will not be able to handle any other requests). I'd generally recommend to separate your network/IO logic from user interface logic. To circumvent the blocking behaviour of I've implemented a very basic image_viewer, which waits for incoming images in a thread that runs separately from the server loop by passing images through a Queue.
The client code looks as follows:
import socket
from PIL import Image
def send_image(img: Image, host: str = '127.0.0.1', port: int = 999):
s = socket.socket()
s.connect((host, port))
img_data = img._repr_png_()
s.sendall(img_data)
s.shutdown(socket.SHUT_WR) # close socket for writing, receiving is still possible
print(f'Sent {len(img_data) / 1024:,.1f} kB of image data.')
b_data = b''
while recv_data := s.recv(2048):
b_data += recv_data
print(f'Server response: {b_data.decode()}')
# maybe check server response for server side errors etc. and add return value for this function?
# use like: send_image(Image.open('test.png'))
The server code is:
import io
import queue
import socket
import threading
import cv2
import numpy as np
from PIL import Image
def image_viewer(q: queue.Queue):
while True:
try:
img_name, img = q.get(block=True, timeout=.1) # poll every 0.1 seconds
print(f'Image viewer: displaying `{img_name}`!')
cv2.imshow('Image preview', img)
except queue.Empty:
... # no new image to display
key = cv2.pollKey() # non-blocking
if key & 0xff == ord('q'):
cv2.destroyAllWindows()
print('Image viewer was closed')
return
def serve_forever(host: str, port: int, img_dir: str = 'C:/Users/my_user/stream_images/', img_format: str = '.png'):
q = queue.Queue()
img_viewer = threading.Thread(target=image_viewer, args=(q,))
img_viewer.start()
with socket.socket() as s:
s.bind((host, port))
s.listen(1)
count = 0
print('The server is ready')
while True:
conn, addr = s.accept()
count = count + 1
img_name = img_dir + 'frame' + str(count) + img_format
print (f'Client connected: {addr}')
img = b''
while data := conn.recv(2048):
img += data
conn.sendall('Thank you for connecting'.encode()) # maybe use return codes for success, error etc.?
conn.close()
pil_img = Image.open(io.BytesIO(img)) # might want to save to disk?
np_img = np.asarray(pil_img)
np_img = cv2.rotate(np_img, cv2.ROTATE_90_CLOCKWISE)
q.put((img_name, np_img))
print (f'Client at {addr} disconnected after receiving {len(img) / 1024:,.1f} kB of data.')
if __name__ == '__main__':
serve_forever('127.0.0.1', 999)

Related

How can i stream my screen over another virtual machine in cloud using python?

I am trying to capture gameplay over my computer and wants the live captured gameplay to be sent to my cloud instance for the purpose of object detection.
I have tried the following code.It works well locally but when I tunnel it using ngrok I am getting
getaddress error.
Is there any other way which I can send the video frames at good FPS over the internet to my cloud instance for performing deep learning processing on it?
client.py
import cv2
import io
import socket
import struct
import time
import pickle
import zlib
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#client_socket.connect(('localhost', 8485))
#Tring to connect to the hosted address obtained after using ngrok http 8485 command.
client_socket.connect(('https://fc766cc3.ngrok.io',0))
connection = client_socket.makefile('wb')
cam = cv2.VideoCapture(0)
cam.set(3, 320);
cam.set(4, 240);
img_counter = 0
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90]
while True:
ret, frame = cam.read()
result, frame = cv2.imencode('.jpg', frame, encode_param)
# data = zlib.compress(pickle.dumps(frame, 0))
data = pickle.dumps(frame, 0)
size = len(data)
print("{}: {}".format(img_counter, size))
client_socket.sendall(struct.pack(">L", size) + data)
img_counter += 1
cam.release()
server.py
import socket
import sys
import cv2
import pickle
import numpy as np
import struct ## new
import zlib
HOST='localhost'
PORT=8485
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print('Socket created')
s.bind((HOST,PORT))
print('Socket bind complete')
s.listen(10)
print('Socket now listening')
conn,addr=s.accept()
data = b""
payload_size = struct.calcsize(">L")
print("payload_size: {}".format(payload_size))
while True:
while len(data) < payload_size:
print("Recv: {}".format(len(data)))
data += conn.recv(4096)
print("Done Recv: {}".format(len(data)))
packed_msg_size = data[:payload_size]
data = data[payload_size:]
msg_size = struct.unpack(">L", packed_msg_size)[0]
print("msg_size: {}".format(msg_size))
while len(data) < msg_size:
data += conn.recv(4096)
frame_data = data[:msg_size]
data = data[msg_size:]
frame=pickle.loads(frame_data, fix_imports=True, encoding="bytes")
frame = cv2.imdecode(frame, cv2.IMREAD_COLOR)
cv2.imshow('ImageWindow',frame)
cv2.waitKey(1)
FIRST OF ALL
You have generated the wrong Link for the client to connect to i.e
#Tring to connect to the hosted address obtained after using ngrok http 8485 command.
client_socket.connect(('https://fc766cc3.ngrok.io',0))
You neeed to use TCP instead of HTTP when using NGROK . Because the Http is used when you are hosting a web app on your server . so the client can go over to any browser paste your provided link and port and connect to your website.
And you used port 0 , I maybe wrong but i dont think portzr 0 is useable
and NGROK does not provide you port 0.
AND SECOND :
Even if you do get a TCP link NGROK does not allow Screen sharing over their Free server . You are able to send small messages or files but Screen Sharing is not possible .

How do I shutdown a socket properly that is streaming webcam footage?

I am trying to stream my laptops webcam stream to my pc for cv2 manipulation. The stream works but I am not really sure how to go about closing the socket.
I don't have a webcam on my desktop so I wanted to try and stream my laptops webcam over to learn cv2 on the footage. I am new to sockets, I went through the documentation and did a bunch of googling to get this far, even though most of what I now have was copy pasted. I am not really sure where to look to learn how to shut down sockets propery when streaming video data with cv2.
I've tried using with socket.socket( ... ) as s: to close the connection
breaks after the while and try loops, that go into shutdown(socket.SHUT_WR and close() and I've tried terminating the program on both the client and server.
#server.py
import socket
import cv2
import pickle
import struct
def main():
HOST=''
PORT=12397
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
print('Socket created')
s.bind((HOST,PORT))
print('Socket bind complete')
s.listen(10)
print('Socket now listening')
conn,addr=s.accept()
data = b""
payload_size = struct.calcsize(">L")
while True:
try:
key = cv2.waitKey(20) & 0xFF
while len(data) < payload_size:
data += conn.recv(4096)
if key == ord("q"):
print("Socket closed.")
break
packed_msg_size = data[:payload_size]
data = data[payload_size:]
msg_size = struct.unpack(">L", packed_msg_size)[0]
while len(data) < msg_size:
data += conn.recv(4096)
if key == ord("q"):
print("Socket closed.")
break
frame_data = data[:msg_size]
data = data[msg_size:]
frame=pickle.loads(frame_data, fix_imports=True, encoding="bytes")
frame = cv2.imdecode(frame, cv2.IMREAD_COLOR)
cv2.imshow('ImageWindow',frame)
cv2.waitKey(1)
if key == ord("q"):
print("Socket closed.")
break
except:
cv2.destroyAllWindows()
break
s.shutdown(socket.SHUT_WR)
s.close()
if __name__ == "__main__":
main()
#client.py
import socket
import cv2
import pickle
import struct
def main():
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90]
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as client_socket:
print('Connecting to socket.')
client_socket.connect((ENTER IP HERE, 12397))
connection = client_socket.makefile('wb')
print('Starting stream.')
cam = cv2.VideoCapture(0)
cam.set(3, 320*2);
cam.set(4, 240*2);
frame_time = int((1.0 / 30.0) * 1000.0)
img_counter = 0
while True:
try:
ret, frame = cam.read()
if ret:
result, frame = cv2.imencode('.jpg', frame, encode_param)
data = pickle.dumps(frame, 0)
size = len(data)
client_socket.sendall(struct.pack(">L", size) + data)
img_counter += 1
if cv2.waitKey(frame_time) & 0xFF == ord('q'):
break
else:
break
except KeyboardInterrupt:
break
cam.release()
client_socket.shutdown(socket.SHUT_WR)
client_socket.close()
if __name__ == "__main__":
main()
I want to close the connection by pressing q.
I currently get WinErrors on the server
OSError: [WinError 10038] An operation was attempted on something that is not a socket
and on the client
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
when I (spam) "q" on the server side, it doesn't seem to catch if I just press it once.
That error typically happen if you use the socket after you have closed it.
Now if we take your server, you do something like
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
... code here ...
s.shutdown(socket.SHUT_WR)
s.close()
That means the shutdown and close calls are done after the socket have already been closed.
You have the same problem in the client program.

python3 tcp client server communication

I want to send an image (.pgm) via TCP as soon as it is written to the ramdisk. For this I'm working with pyinotify and sockets. After the picture is sent I would like to tell the server to stop now.
Everything works fine but the last part gives me following error:
if data.decode('utf-8') == 'stop': UnicodeDecodeError: 'utf-8' codec can't
decode byte 0x88 in position 319: invalid start byte
Client:
import pyinotify
import socket
import traceback
import sys
class ModHandler(pyinotify.ProcessEvent):
def __init__(self, socket, buffer_size):
self.socket = socket
self.buffer_size = buffer_size
def process_IN_CLOSE_WRITE(self, event):
try:
self.socket.send(bytes(event.pathname, encoding='utf-8'))
file = open(event.pathname, "rb")
line = file.read(self.buffer_size)
while(line):
self.socket.send(line)
line = file.read(self.buffer_size)
except Exception:
traceback.print_exc()
finally:
try:
self.socket.send(bytes('stop', encoding='utf-8'))
print("done")
file.close
except Exception:
traceback.print_exc()
class TCPStream():
def __init__(self, ip, port, buffer_size):
self.ip = ip
self.port = port
self.buffer_size = buffer_size
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.socket.connect((self.ip, self.port))
except Exception:
traceback.print_exc()
def __del__(self):
try:
self.socket.close()
except Exception:
traceback.print_exc()
stream = TCPStream('127.0.0.1', 5005, 1024)
handler = ModHandler(stream.socket, stream.buffer_size)
wm = pyinotify.WatchManager()
notifier = pyinotify.Notifier(wm, handler)
wd_value = wm.add_watch("/media/ram_disk", pyinotify.IN_CLOSE_WRITE)
if wd_value["/media/ram_disk"] <= 0:
print("can't add watchmanager to the ram_disk... insufficient
authorization? another watchmanager already running?")
sys.exit(0)
notifier.loop()
Server:
import socket
TCP_IP = '127.0.0.1'
TCP_PORT = 5005
BUFFER_SIZE = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
conn, addr = s.accept()
print("connection address: ", addr)
path = conn.recv(BUFFER_SIZE).decode('utf-8')
filename = path.split("/")
with open(filename[3], 'wb') as f:
data = conn.recv(BUFFER_SIZE)
while data:
print("receiving...")
f.write(data)
data = conn.recv(BUFFER_SIZE)
if not data:
break
if data.decode('utf-8') == 'stop':
f.close()
print("done")
break
conn.close()
The goal is to have a constant TCP stream of images written to the ramdisk. Therefore I wanted to communicate via Bytes with the server to tell him what to do. It seems that after the first picture gets transmitted it breaks somehow. Any help is appreciated!
What if four consecutive bytes in your image at the beginning of a buffer happen to match the ASCII (and UTF-8) characters s t o p? Also, how does the receiving side know when the file name ends, and the file data starts?
You should create a binary encoding that frames the various bits of your data. This makes the whole process nicely deterministic. That's often best done with the struct module's pack and unpack methods. Given a file you want to send, client side:
import os
import struct
...
pathname = event.pathname.encode('utf-8') # Encode pathname into bytes
pathname_len = len(pathname)
file = open(event.pathname, "rb")
filesize = os.path.getsize(event.filename) # Get file size
# Encode size of file name, and size of file into a binary header
header_format = struct.Struct("!II")
header = header_format.pack(pathname_len, filesize)
self.socket.sendall(header)
self.socket.sendall(pathname)
while True:
line = file.read(self.buffer_size)
if not line: break # EOF
self.socket.sendall(line)
# (Remove sending of 'stop' from finally block)
Note the use of sendall to ensure that the entire buffer gets sent (it is legal for send to send only part of a buffer but that can result in missing bytes if you don't account for it).
Server side will look something like this:
import struct
...
def recv_exactly(s, buffer_len):
""" This is the converse of sendall """
data = b''
rem_bytes = buffer_len
while rem_bytes > 0:
buf = s.recv(rem_bytes)
if not buf:
raise Exception("Received EOF in middle of block")
data += buf
rem_bytes -= len(buf)
return data
conn, addr = s.accept()
...
header_format = struct.Struct("!II")
# Receive exactly the bytes of the header
header = recv_exactly(conn, header_format.size)
pathname_len, file_len = header_format.unpack(header)
path = recv_exactly(conn, pathname_len)
filename = path.split("/")
...
rem_bytes = file_len
while rem_bytes > 0:
data = conn.recv(min(rem_bytes, BUFFER_SIZE))
if not data:
raise Exception("Received EOF in middle of file")
f.write(data)
rem_bytes -= len(data)
Another important advantage of this model is that you now have a clear notion of the boundary between one file and the next (without having a "signal value" that might appear in the data). The receiver always knows exactly how many bytes remain until the end of the current file, and the sender can simply move on to send a new header, pathname, and file without opening a new connection.

Sending Mouse and keyboard inputs from server to client over socket in Python?

I have my desktop stream working the server can see the client desktop, what I basically do is grab screenshots from client and send it to server continuously and displaying it using cv2.imshow().
Now I want a way to send mouse and keyboards inputs from server to the client so that it can control the client remotely. I know pyautogui module could be used to simulate moving, click and dragging mouse as well as pressing keys.
One way I thought is to send the coordinates in fractions of the resolution.
Example:
If your picturebox is 800x600 and you click at location (200, 300), send (0.25, 0.5).
On the other machine, you receive (0.25, 0.5) and you multiply them by the resolution. On a 1024x768, you move the mouse pointer to location (256, 384).
But I need a method to get the mouse and keyboard input from the opencv window and send it to the client over the socket and then simulate it on client side.
Any other method instead of using opencv to display the frames on server would be accepted as well, as long as I am able to work with keyboard and mouse inputs and improves the performance.
Note: I am also not able to get the screenshot of the UAC prompt using Imagegrab.grab() even if I run the script as an administrator I don't know why
this is happening.
Below is my code for client and server exceptions which are not being handled as I am just trying out that the things work.
Any suggestions to make my stream faster are welcomed as well :)
server.py:
import socket
import struct
import cv2
import numpy
host = "192.168.0.5"
port = 4444
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #create socket
s.bind((host,port))
s.listen(5)
c , addr = s.accept()
print("Connection recieved from {}".format(addr))
cv2.namedWindow("Remote Desktop",cv2.WINDOW_NORMAL)
while True:
bs = c.recv(8)
(length,) = struct.unpack(">Q",bs) # get length of image
data = b''
while(len(data)<length): # get image data
recv_data = length - len(data)
if(recv_data>4096):
data += c.recv(4096)
else:
data += c.recv(recv_data)
with open("demo.jpg","wb") as f:
d = f.write(data)
#print(d)
img = cv2.imread("demo.jpg")
cv2.imshow("Remote Desktop",img)
key = cv2.waitKey(1)
if key == 27:
break
cv2.destroyAllWindows()
s.close()
Here is my client.py:
import socket
import struct
from PIL import ImageGrab
import time
host = "192.168.0.5"
port = 4444
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #create socket
s.connect((host,port)) #connect to server
print("Connection Established")
time.sleep(5)
while True:
img = ImageGrab.grab()
img.save("demo.jpg","JPEG")
with open("demo.jpg","rb") as f:
data = f.read() # read the image
s.sendall(struct.pack(">Q",len(data))) #send the length of image in bytes
s.sendall(data) # send the data
s.close()
So how can I send mouse and keyboard inputs effectively over socket in python? Thanks for your help.
This can help you out....Make it according to you need.
import threading
import mouse
import keyboard
mouse_events = []
mouse.hook(mouse_events.append)
keyboard.start_recording()
keyboard.wait("*")
mouse.unhook(mouse_events.append)
keyboard_events = keyboard.stop_recording()
#Keyboard threadings:
k_thread = threading.Thread(target = lambda :keyboard.play(keyboard_events))
k_thread.start()
#Mouse threadings:
m_thread = threading.Thread(target = lambda :mouse.play(mouse_events))
m_thread.start()
#waiting for both threadings to be completed
k_thread.join()
m_thread.join()

Minimize lag in video streaming over TCP in python

I am doing a research using raspberry pi. I want to stream video from my raspberry pi (Client) to my laptop (Server) for my project. After two days research i have found following code.
CLIENT
import socket
import cv2
import pickle
import struct
device = cv2.VideoCapture(-1)
ok, frame = device.read()
host = '192.168.1.4'
port = 12345
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((host, port))
while True:
ok, frame = device.read()
data = pickle.dumps(frame)
client_socket.sendall(struct.pack("L", len(data)) + data)
cv2.imshow("",frame)
if cv2.waitKey(1) == 27:
break
device.release()
cv2.destroyAllWindows()
SERVER
import socket
import cv2
import numpy as np
import time
import pickle
import struct
host = '192.168.1.4'
port = 12345
client_socket = None
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((host, port))
server_socket.listen(5)
conn, addr = server_socket.accept()
data = b''
payload_size = struct.calcsize("L")
while True:
while len(data) < payload_size:
#data += conn.recv(90456)
data += conn.recv(4096)
packed_msg_size = data[:payload_size]
data = data[payload_size:]
msg_size = struct.unpack("L", packed_msg_size)[0]
while len(data) < msg_size:
data += conn.recv(4096)
frame_data = data[:msg_size]
data = data[msg_size:]
frame = pickle.loads(frame_data)
#print (frame.size)
cv2.imshow('frame', frame)
cv2.waitKey(1)
But there is a big lag. What i want is live streaming (if possible) or as much as close to live streaming (with minimum lag). Someone could suggest UDP but since it not guarantee that getting all frames.
Can some one suggest me to improve my code. Any help??
Thank you very much in advance!!

Resources