Example #1
0
def mc_recv_file(fromnicip, mcgrpip, mcport_recv, mcport_send, use_buffer):
    # 1. Get a socket to receive data with and a socket to send data with
    receiver = common.prep_receiver(fromnicip, mcgrpip, mcport_recv)
    endpoint = (mcgrpip, mcport_send)
    sender = common.prep_sender(fromnicip)
    # 2. Receive metadata
    filename, datasize, remote_checksum = recv_metadata(receiver, sender, \
            endpoint)

    # The buffer is just a hash table, where each key is a sequence number and
    # each value is the file content in bytes; this is only used if the option
    # is chosen by user
    content_buffer = {}

    # 3. Receive file content
    more = '1'
    while True:
        if more == '1':
            packet, opcode, _ = \
                    common.get_packet(receiver, common.MAX_PACKET_SIZE, None)
            if opcode != common.CONTENT_OP:
                continue
        else:
            break
        seq_num = int(packet[1:common.SEQ_NUM_LEN + 1])
        print("Received file packet number:", seq_num)
        more = packet[common.SEQ_NUM_LEN + 1:common.SEQ_NUM_LEN + 2]
        content = packet[common.SEQ_NUM_LEN + 2:common.BLOCKSIZE \
                + common.SEQ_NUM_LEN + 2]
        if not use_buffer:
            write_bytes(filename, common.BLOCKSIZE, seq_num, content.encode())
        else:
            content_buffer[seq_num] = content.encode()

    # Write the buffer to the file if user picked this option
    if use_buffer:
        write_buffer(filename, content_buffer)

    # 4. Check if local data matches remote and send feedback
    error = None
    local_checksum = common.hash_file(common.BLOCKSIZE, filename)
    if local_checksum != remote_checksum:
        sender.sendto((common.FEEDBACK_OP + common.ERROR_OP).encode(),
                      endpoint)
        error = True
    else:
        sender.sendto((common.FEEDBACK_OP + common.DONE_OP).encode(), endpoint)
        error = False
    print("Checksum of local data:", local_checksum)

    # 5. Release socket resources and indicate success/error to user
    common.release_rsrcs([sender, receiver])
    if error:
        common.exit_err(f'Error: could not properly receive {filename}, ' \
                + 'aborting...\n', 1)
    else:
        print(f'Completed: received {filename}\n', file=sys.stderr)
Example #2
0
def mc_send_file(hostip, mcgrpip, mcport_send, mcport_recv, filename):
    datasize = os.stat(filename).st_size
    print(f'Sending file: {filename}\n')
    if datasize > (common.MAX_SEQ * common.BLOCKSIZE):
        print('WARNING: data size exceeds 1-petabyte, transmitted data will be'\
                + ' truncated!', file=sys.stderr)
    # 1. Get a socket to send data with and a socket to receive data with
    endpoint = (mcgrpip, mcport_send)
    sender = common.prep_sender(hostip)
    receiver = common.prep_receiver(hostip, mcgrpip, mcport_recv)
    # 2. Send metadata
    send_metadata(sender, endpoint, receiver, filename, datasize)

    # 3. Read the file and the transmit the file content in packets of BLOCKSIZE
    # data length; the single-byte prefix for file content is CONTENT_OP
    with open(filename, 'rb') as f:
       seq_num = 1
       more = '0'
       packets_left = count_blocks(common.BLOCKSIZE, datasize, filename)
       content_bytes = f.read(common.BLOCKSIZE)
       while content_bytes and seq_num <= common.MAX_SEQ:
           content_sent = False
           more = '1' if packets_left > 1 else '0'
           packet = common.CONTENT_OP.encode() \
                   + str(seq_num).zfill(common.SEQ_NUM_LEN).encode() \
                   + more.encode() \
                   + content_bytes
           while not content_sent:
               sender.sendto(packet, endpoint)
               feed_packet, opcode, timedout = \
                       common.get_packet(receiver, common.MAX_PACKET_SIZE, \
                       common.BLOCKING_WAIT)
               if not timedout and opcode == common.FEEDBACK_OP and \
                       feed_packet[1:common.SEQ_NUM_LEN + 1] == \
                       str(seq_num).zfill(common.SEQ_NUM_LEN):
                   content_sent = True
           seq_num += 1
           packets_left -= 1
           content_bytes = f.read(common.BLOCKSIZE)
           time.sleep(common.PACKET_DELAY)

    # 4. Handle final feedback and release socket resources
    while True:
        packet, opcode, timedout = \
                common.get_packet(receiver, common.MAX_PACKET_SIZE, \
                common.BLOCKING_WAIT)
        if not timedout and opcode == common.FEEDBACK_OP:
            break
    print('Final feedback:', packet[1])
    common.release_rsrcs([sender, receiver])
    if packet[1] == common.ERROR_OP:
        common.exit_err(f'Error: could not properly send {filename}, ' \
                + 'aborting...\n', 1)
    elif packet[1] == common.DONE_OP:
        print(f'\nCompleted: sent {filename}', file=sys.stderr)
    else:
        print('Unable to confirm final transmission status.')
Example #3
0
def send_metadata(sender, endpoint, receiver, filename, datasize):
    filename_sent = False
    datasize_sent = False
    checksum_sent = False
    # Transmit the filename in a packet
    # The filename is a byte string of FNAME_LEN length; the single-byte prefix
    # for filename is FNAME_OP
    fname_bytes = (common.FNAME_OP + filename[:common.FNAME_LEN]).encode()
    print('Filename sent:', fname_bytes)
    while not filename_sent:
        sender.sendto(fname_bytes, endpoint)
        packet, opcode, timedout = \
                common.get_packet(receiver, common.MAX_PACKET_SIZE, \
                common.BLOCKING_WAIT)
        if not timedout and opcode == common.FEEDBACK_OP \
                and packet[1] == common.ACK_OP:
            filename_sent = True
    print('Filename feedback:', packet[1])

    # Transmit the data size in a packet
    # The data size is a byte string of DATASIZE_LEN length; the single-byte
    # prefix for data size is DATASIZE_OP
    datasize_bytes = (common.DATASIZE_OP \
            + str(datasize)[:common.DATASIZE_LEN]).encode()
    print('Data size sent:', datasize_bytes)
    while not datasize_sent:
        sender.sendto(datasize_bytes, endpoint)
        packet, opcode, timedout = \
                common.get_packet(receiver, common.MAX_PACKET_SIZE, \
                common.BLOCKING_WAIT)
        if not timedout and opcode == common.FEEDBACK_OP \
                and (packet[1] == common.ACK_OP or packet[1] == common.STOP_OP):
            datasize_sent = True
    if packet[1] == common.STOP_OP:
        common.release_rsrcs([sender, receiver])
        common.exit_err('Aborting due to receiver request...', 0)
    print('Data size feedback:', packet[1])

    # Generate and transmit SHA-2 256-bit checksum for file being transmitted in
    # a packet, the hexadecimal digest transmitted is a 64-byte string; the
    # single-byte prefix for checksum is HASH_OP, the string length of the
    # digest is also found as HASH_LEN
    hexdigest = common.hash_file(common.BLOCKSIZE, filename)
    hexdigest_bytes = (common.HASH_OP + hexdigest).encode()
    print('Checksum sent:', hexdigest_bytes)
    while not checksum_sent:
        sender.sendto(hexdigest_bytes, endpoint)
        packet, opcode, timedout = \
                common.get_packet(receiver, common.MAX_PACKET_SIZE, \
                common.BLOCKING_WAIT)
        if not timedout and opcode == common.FEEDBACK_OP \
                and packet[1] == common.ACK_OP:
            checksum_sent = True
    print('Checksum feedback:', packet[1])
Example #4
0
def main(argv):
    if len(argv) != 6:
        common.exit_err('Usage: ' + argv[0] + ' host_ip mcast_group_ip ' \
                + 'mcast_port_send mcast_port_recv file_to_send', 1)
    hostipaddr = argv[1]
    mcgrpipaddr = argv[2]
    mcport_send = int(argv[3])
    mcport_recv = int(argv[4])
    filename = argv[5]
    if not os.path.isfile(filename):
        common.exit_err(f'File: {filename} not found!', 1)
    mc_send_file(hostipaddr, mcgrpipaddr, mcport_send, mcport_recv, filename)
Example #5
0
def recv_metadata(receiver, sender, endpoint):
    filename_rcvd = False
    datasize_rcvd = False
    checksum_rcvd = False
    # Check free space on current working directory
    _, _, free = shutil.disk_usage(__file__)
    # Wait for metadata
    while not filename_rcvd or not datasize_rcvd or not checksum_rcvd:
        packet, opcode, _ = \
                common.get_packet(receiver, common.MAX_PACKET_SIZE, None)
        if opcode == common.FEEDBACK_OP and packet[1] == common.STOP_OP:
            common.release_rsrcs([sender, receiver])
            common.exit_err('Aborting due to sender request...', 0)
        # Receive the filename packet
        # The filename is a byte string of FNAME_LEN length; the single-byte
        # prefix for filename is FNAME_OP
        if opcode == common.FNAME_OP:
            filename = packet[1:common.FNAME_LEN + 1]
            filename_rcvd = True
            print("Filename received:", filename)
            sender.sendto((common.FEEDBACK_OP + common.ACK_OP).encode(), \
                    endpoint)
        # Receive the data size packet
        # The data size is a byte string of DATASIZE_LEN length; the single-byte
        # prefix for data size is DATASIZE_OP
        if opcode == common.DATASIZE_OP:
            datasize = int(packet[1:common.DATASIZE_LEN + 1])
            datasize_rcvd = True
            print("Data size received:", datasize)
            if free < datasize:
                # Tell sender to stop sending anything and exit.
                common.make_clean_exit(sender, receiver, (common.FEEDBACK_OP \
                        + common.STOP_OP).encode(), endpoint, f'Not enough ' \
                        + 'free space available for file, aborting...', 1)
            sender.sendto((common.FEEDBACK_OP + common.ACK_OP).encode(), \
                    endpoint)
        # Receive the remote checksum packet
        # The checksum is a 64-byte string hexadecimal digest; the single-byte
        # prefix for checksum is HASH_OP, the string length of the digest is
        # also found as HASH_LEN
        if opcode == common.HASH_OP:
            checksum = packet[1:common.HASH_LEN + 1]
            checksum_rcvd = True
            print("Checksum received:", checksum)
            sender.sendto((common.FEEDBACK_OP + common.ACK_OP).encode(), \
                    endpoint)
    return (filename, datasize, checksum)
Example #6
0
def help_and_exit(argv):
    common.exit_err('Usage: ' + argv[0] + ' -i|-b from_nic_by_host_ip ' \
            + 'mcast_group_ip mcast_port_recv mcast_port_send', 1)
Example #7
0
def send_metadata(sender, endpoint, receiver, filename, datasize):
    # Transmit the filename in a packet
    # The filename is a byte string of FNAME_LEN length; the single-byte prefix
    # for filename is FNAME_OP
    fname_bytes = (common.FNAME_OP + filename[:common.FNAME_LEN]).encode()
    print('Filename sent:', fname_bytes)
    sender.sendto(fname_bytes, endpoint)
    packet, opcode, timedout = \
            common.get_packet(receiver, common.MAX_PACKET_SIZE, \
            common.BLOCKING_WAIT)
    if timedout:
        common.make_clean_exit(sender, receiver, (common.FEEDBACK_OP \
                + common.STOP_OP).encode(), endpoint, \
                'Timed out, aborting...', 1)
    if opcode != common.FEEDBACK_OP:
        common.make_clean_exit(sender, receiver, (common.FEEDBACK_OP \
                + common.STOP_OP).encode(), endpoint, \
                'Feedback invalid, aborting', 1)
    print('Filename feedback:', packet[1])

    # Transmit the data size in a packet
    # The data size is a byte string of DATASIZE_LEN length; the single-byte
    # prefix for data size is DATASIZE_OP
    datasize_bytes = (common.DATASIZE_OP \
            + str(datasize)[:common.DATASIZE_LEN]).encode()
    print('Data size sent:', datasize_bytes)
    sender.sendto(datasize_bytes, endpoint)
    packet, opcode, timedout = \
            common.get_packet(receiver, common.MAX_PACKET_SIZE, \
            common.BLOCKING_WAIT)
    if timedout:
        common.make_clean_exit(sender, receiver, (common.FEEDBACK_OP \
                + common.STOP_OP).encode(), endpoint, \
                'Timed out, aborting...', 1)
    if opcode != common.FEEDBACK_OP:
        common.make_clean_exit(sender, receiver, (common.FEEDBACK_OP \
                + common.STOP_OP).encode(), endpoint, \
                'Feedback invalid, aborting', 1)
    print('Data size feedback:', packet[1])
    if packet[1] == common.STOP_OP:
        common.release_rsrcs([sender, receiver])
        common.exit_err('Aborting due to receiver request...', 0)

    # Generate and transmit SHA-2 256-bit checksum for file being
    # transmitted in a packet, the hexadecimal digest transmitted is a
    # 64-byte string; the single-byte prefix for checksum is HASH_OP, the
    # string length of the digest is also found as HASH_LEN
    hexdigest = common.hash_file(common.BLOCKSIZE, filename)
    hexdigest_bytes = (common.HASH_OP + hexdigest).encode()
    print('Checksum sent:', hexdigest_bytes)
    sender.sendto(hexdigest_bytes, endpoint)
    packet, opcode, timedout = \
            common.get_packet(receiver, common.MAX_PACKET_SIZE, \
            common.BLOCKING_WAIT)
    if timedout:
        common.make_clean_exit(sender, receiver, (common.FEEDBACK_OP \
                + common.STOP_OP).encode(), endpoint, \
                'Timed out, aborting...', 1)
    if opcode != common.FEEDBACK_OP:
        common.make_clean_exit(sender, receiver, (common.FEEDBACK_OP \
                + common.STOP_OP).encode(), endpoint, \
                'Feedback invalid, aborting', 1)
    print('Checksum feedback:', packet[1])