Home > front end >  sending multiple images using socket python get sent as one to client
sending multiple images using socket python get sent as one to client

Time:09-08

I am capturing screenshots from the server, then sending it to the client, but the images get all sent as one big file to the client that keeps expanding in size. This only happens when i send from one machine to another (I am working on a local netwrok) but when running both client and server from my machine they work fine. Note: for the client on the other machine, I packaged it into an exe using pyinstaller, since this machine does not have python.

server code:

host="192.168.43.79"                # Set the server address to variable host
port=4446                   # Sets the variable port to 4446
import time
import pyautogui
from socket import *
import os
s=socket(AF_INET, SOCK_STREAM)

s.bind((host,port))                 

print("Listening for connections.. ")

q,addr=s.accept()
            
i = 0                      
while True:

    screenshot = pyautogui.screenshot()
    screenshot.save(str(i)   ".jpg")
    with open(str(i)   ".jpg", "rb") as f:
        data = f.read(4096)
        while data:
            q.send(data)
            data = f.read(4096)
    q.send(b"full")
    i  = 1
    time.sleep(0.3)           


client code:


host="192.168.43.79"            # Set the server address to variable host
 
port=4446               # Sets the variable port to 4446
 
from multiprocessing.reduction import recv_handle
from socket import *             # Imports socket module
 
s=socket(AF_INET, SOCK_STREAM)      # Creates a socket
s.connect((host,port)) 

i = 0
while True:
    with open(str(i)   "s.jpg", "wb") as f:
        recv_data = s.recv(4096)            
        while recv_data:
            f.write(recv_data)
            recv_data = s.recv(4096)
            if(recv_data == b"full"):
                break
    i  = 1

CodePudding user response:

There various wrong assumptions here which lead to the problem you see. The wrong assumptions are:

  • that send(data) will write all data
    It might send less. You need to check the return value or use sendall to be sure.
  • that a single send in the sender is matched by exactly a single recv in the recipient
    TCP is only an unstructured byte stream. send does not add message semantics, so a single send might lead to multiple recv, multiple send might lead to a single recv etc. Specifically send("data") followed by send("full") might be recv(4096) as "datafull", thus missing your code to detect end of image.

As for why does it work on the local machine but not on the remote - the chance in the latter case is higher that send get combined together and recv as one.

CodePudding user response:

As stated by Steffen Ulrich you should use sendall for sending and for receiving we create a specialized function my_recv that will repeatedly call socket.recv until the expected number of bytes have been received. Also, a 4-byte header (you can make the length greater if your file sizes warrant this) that contains a binary representation of the file length precedes the sending of the actual file data. In this way the client knows exactly how much data it should receive for each file.

Server Code

host="192.168.43.79"                # Set the server address to variable host
port=4446                   # Sets the variable port to 4446
import time
import pyautogui
from socket import *
import os
s=socket(AF_INET, SOCK_STREAM)

s.bind((host,port))
s.listen(1) # This should be called

print("Listening for connections.. ")

q,addr=s.accept()

i = 0
while True:

    screenshot = pyautogui.screenshot()
    screenshot.save(str(i)   ".jpg")
    with open(str(i)   ".jpg", "rb") as f:
        # Get length by positioning to end of file
        image_length = f.seek(0, 2)
        f.seek(0, 0) # Seek back to beginning of file
        # Convert image length to a 4-byte array:
        image_length_bytes = image_length.to_bytes(4, 'big')
        q.sendall(image_length_bytes)
        data = f.read(4096)
        while len(data):
            q.sendall(data)
            data = f.read(4096)
    i  = 1

Client Code

host="192.168.43.79"            # Set the server address to variable host

port=4446               # Sets the variable port to 4446

from multiprocessing.reduction import recv_handle
from socket import *             # Imports socket module

s=socket(AF_INET, SOCK_STREAM)      # Creates a socket
s.connect((host,port))

def my_recv(msg_length):
    chunks = []
    bytes_to_recv = msg_length
    while bytes_to_recv:
        chunk = s.recv(bytes_to_recv)
        if chunk == b'':
            raise RuntimeError("socket connection broken")
        chunks.append(chunk)
        bytes_to_recv -= len(chunk)
    return b''.join(chunks)

i = 0
while True:
    image_length_bytes = my_recv(4)
    image_length = int.from_bytes(image_length_bytes, 'big')
    with open(str(i)   "s.jpg", "wb") as f:
        bytes_to_recv = image_length
        while bytes_to_recv:
            recv_data = my_recv(min(4096, bytes_to_recv))
            f.write(recv_data)
            bytes_to_recv -= len(recv_data)

    i  = 1
  • Related