1

So my project is that I need to send a jpg image from one computer to another computer in the same network. To send the data I split the data into chunks of at least 9999 bytes and then I create a length header that tells the length of the data and I attach it to the start of the massage. here is the code:

the protocol:

    import os.path
LENGTH_FIELD_SIZE = 4
PORT = 8820

COMANDS_LIST = "TAKE_SCREENSHOT\nSEND_PHOTO\nDIR\nDELETE\nCOPY\nEXECUTE\nEXIT".split("\n")
def check_cmd(data):
    """
    Check if the command is defined in the protocol, including all parameters
    For example, DELETE c:\work\file.txt is good, but DELETE alone is not
    """

    command = ""
    file_location =""

    splited_data = data.split(maxsplit=1)

    if len(splited_data) == 2:
        command, file_location = splited_data
        return (command in COMANDS_LIST) and (file_location is not None)

    elif len(splited_data) == 1:
        command = splited_data[0]

        return command in ["TAKE_SCREENSHOT","EXIT","SEND_PHOTO"]

    return False

    # (3)


def create_msg(data):
    """
    Create a valid protocol message, with length field
    """
    data_len = len(str(data))
    if data_len > 9999 or data_len == 0:
        print(f"data len is bigger then 9999 or is 0, data len = {data_len} ")
        return False
    len_field = str(data_len).zfill(4)



    # (4)
    print(len_field)
    return True ,f"{len_field}{data}"


def get_msg(my_socket):
    """
    Extract message from protocol, without the length field
    If length field does not include a number, returns False, "Error"
    """
    lenght_field = ""
    data = ""
    try:
        while len(lenght_field) < 4:
            lenght_field += my_socket.recv(4).decode()

    except RuntimeError as exc_run:
        return False, "header wasnt sent properly"

    if not lenght_field.isdigit():
        return False, "error, length header is not valid"

    lenght_field = lenght_field.lstrip("0")

    while len(data) < int(lenght_field):
        data += my_socket.recv(int(lenght_field)).decode()

    return True, data

now the protocol works fine when I use the same computer for both server and client and when I debug get_msg on the other computer. when I'm not, it seems that the problem is that the part that recv the header will recv something else after a few successful recv and return an error message.

here are the server parts:

import socket

import pyautogui as pyautogui

import protocol
import glob
import os.path
import shutil
import subprocess
import base64


IP = "0.0.0.0"
PORT = 8820
PHOTO_PATH = r"C:\Users\Innon\Pictures\Screenshots\screenShot.jpg"# The path + filename where the screenshot at the server should be saved


def check_client_request(cmd):
    """
    Break cmd to command and parameters
    Check if the command and params are good.

    For example, the filename to be copied actually exists

    Returns:
        valid: True/False
        command: The requested cmd (ex. "DIR")
        params: List of the cmd params (ex. ["c:\\cyber"])
    """
    # Use protocol.check_cmd first
    cmd_arr = cmd.split(maxsplit=1)
    command = cmd_arr[0]
    file_location = None

    if len(cmd_arr) == 2:
         file_location = cmd_arr[1]

    if file_location == None:
        return protocol.check_cmd(cmd) ,command, file_location

    else:
        file_location = tuple(str(file_location).split())
        if (os.path.exists(file_location[0])):
            return protocol.check_cmd(cmd) , command , file_location
        return False , command , file_location
    # Then make sure the params are valid
    # (6)

def handle_client_request(command,params):
    """Create the response to the client, given the command is legal and params are OK

    For example, return the list of filenames in a directory
    Note: in case of SEND_PHOTO, only the length of the file will be sent

    Returns:
        response: the requested data

    """

    # (7)
    response = "no server response"
    if command == "DIR":
        response = glob.glob(f"{params[0]}\\*.*" )

    if command == "DELETE":
        os.remove(params[0])
        response = f"{params[0]} was deleted"

    if command == "COPY":
        try:
            shutil.copy(params[0],params[1])
            response = f"{params[0]} was copyed to {params[1]}"
        except FileNotFoundError as ex1:
            response = ex1
        except IndexError as ex2:
            response = ex2

    if command == "EXECUTE":
        subprocess.call(params[0])
        response = f"{params[0]} was executed"

    if command == "TAKE_SCREENSHOT":
        #todo find a way to know and create the locatipn of screen shot to be saved
        myScreenshot = pyautogui.screenshot()
        myScreenshot.save(PHOTO_PATH)
        response = f"screen shot have been taken and been saved at {PHOTO_PATH}"

    if command == "SEND_PHOTO":
        with open(PHOTO_PATH, "rb") as file:
            file_data =  base64.b64encode(file.read()).decode()
            print(file_data)
            is_vaild_response, img_length = protocol.create_msg(len(file_data))
            print(img_length)
            img_data = ""

            if not is_vaild_response:
                response = "img length data isnt valid"
                return response

            while len(file_data) > 0:
                chunk_data = file_data[:9999]
                is_vaild_response, data = protocol.create_msg(chunk_data)
                if not is_vaild_response:
                    response = "img data isnt valid"
                    return response
                img_data += data
                file_data = file_data[9999:]
            response = f"{img_length}{img_data}"


    return response


def main():
    # open socket with client
    server_socket = socket.socket()
    server_socket.bind((IP,PORT))
    server_socket.listen(1)
    # (1)
    client_socket, addr = server_socket.accept()

    # handle requests until user asks to exit
    while True:
        # Check if protocol is OK, e.g. length field OK
        valid_protocol, cmd = protocol.get_msg(client_socket)
        print(f"got message {valid_protocol}")

        if valid_protocol:
            # Check if params are good, e.g. correct number of params, file name exists
            valid_cmd, command, params = check_client_request(cmd)
            print(f"check_client_request {valid_cmd}")
            if valid_cmd:

                # (6)
                if command == 'EXIT':
                    break

                if command == 'SEND_PHOTO':
                    data = handle_client_request(command, params)
                    client_socket.sendall(data.encode())
                    continue


                # prepare a response using "handle_client_request"
                data = handle_client_request(command,params)
                # add length field using "create_msg"
                is_vaild_response , response = protocol.create_msg(data)
                print(f"creat_msg {is_vaild_response}")
                # send to client
                if is_vaild_response:
                    client_socket.sendall(response.encode())



            else:
                # prepare proper error to client
                resp = 'Bad command or parameters'
                is_vaild_response , response = protocol.create_msg(resp)
                # send to client
                client_socket.sendall(response.encode())


        else:
            # prepare proper error to client
            resp = 'Packet not according to protocol'
            is_vaild_response, response = protocol.create_msg(resp)
            #send to client
            client_socket.sendall(response.encode())

            # Attempt to clean garbage from socket
            client_socket.recv(1024)

    # close sockets
    resp = "Closing connection"
    print(resp)
    is_vaild_response, response = protocol.create_msg(resp)
    client_socket.sendall(response.encode())
    client_socket.close()
    server_socket.close()


if __name__ == '__main__':
    main()
   

and the client:

import socket
import base64


import protocol

IP = "127.0.0.1"
SAVED_PHOTO_LOCATION = r'C:\Users\Innon\Pictures\Saved Pictures\screenShot.jpg' # The path + filename where the copy of the screenshot at the client should be saved

def handle_server_response(my_socket, cmd):
    """
    Receive the response from the server and handle it, according to the request
    For example, DIR should result in printing the contents to the screen,
    Note- special attention should be given to SEND_PHOTO as it requires and extra receive
    """
    

    # (8) treat all responses except SEND_PHOTO
    if "SEND_PHOTO" not in cmd:
        vaild_data, data = protocol.get_msg(my_socket)
        if vaild_data:
            return data
    # (10) treat SEND_PHOTO
    else:
        pic_data = ""
        vaild_pick_len, pic_len = protocol.get_msg(my_socket)

        if pic_len.isdigit() == False:
            print(f"picture length is not valid. got massage: {pic_len}")
            return

        with open(SAVED_PHOTO_LOCATION, "wb") as file:
            while len(pic_data) < int(pic_len):
                vaild_data, data = protocol.get_msg(my_socket)
                if not vaild_data:
                    return f"img data isnt valid. {data}"
                pic_data += data
            print(pic_data)
            file.write(base64.b64decode(pic_data.encode()))
        return "img was recived succesfully "


def main():
    # open socket with the server
    my_socket = socket.socket()
    my_socket.connect((IP,8820))
    # (2)

    # print instructions
    print('Welcome to remote computer application. Available commands are:\n')
    print('TAKE_SCREENSHOT\nSEND_PHOTO\nDIR\nDELETE\nCOPY\nEXECUTE\nEXIT')

    # loop until user requested to exit
    while True:
        cmd = input("Please enter command:\n")
        if protocol.check_cmd(cmd):
            valid_pack , packet = protocol.create_msg(cmd)
            if valid_pack:
                my_socket.sendall(packet.encode())
            print(handle_server_response(my_socket, cmd))

            if cmd == 'EXIT':
                break
        else:
            print("Not a valid command, or missing parameters\n")

    my_socket.close()

if __name__ == '__main__':
    main()

here is how the problem looks like:thi is how it looks

here is how to needs look like: the right way

thank you.

Innon Rivkin
  • 11
  • 1
  • 2

1 Answers1

0

the solution was to change get_msg function in the protocol:

  while len(data) < int(lenght_field):
    data += my_socket.recv(int(lenght_field) - len(data)).decode()

instead of:

while len(data) < int(lenght_field):
    data += my_socket.recv(int(lenght_field)).decode()
Innon Rivkin
  • 11
  • 1
  • 2