Question: Your task is to implement the ReliableMessageReceiver classes in reliable_transport.py. Sender message and receiver have already been implemented. Please implement reliable message receiver on the
Your task is to implement the ReliableMessageReceiver classes in reliable_transport.py. Sender message and receiver have already been implemented. Please implement reliable message receiver on the basis of the code of reliable message sender provided below. CODE IN PYTHON. Implement the window sliding mechanism.
ReliableMessageSender:
This class reliably delivers a message to a receiver. You have to implement the send_message and on_packet_received methods. You can use self.send(packet) to send a packet to the receiver. I am also attaching the reliable socket.py file below for help. You only need to edit the reliable transport.py.
Method descriptions:
__init__(self, ..., window_size: int) This is the constructor of the class where you can define any class attributes. window_size is the size of your message transport window (the number of in-flight packets during message transmission). Ignore other arguments; they are passed to the parent class. You should immediately return from this function and not block.
send_message(self, message: str) This method reliably sends the passed message to the receiver. This method does not need to spawn a new thread and return immediately; it can block indefinitely until the message is completely received by the receiver. You can send a packet to the receiver by calling self.send(...).
on_packet_received(self, packet: str) This method is invoked whenever a packet is received from the receiver. Ideally, only ACK packets should be received here. You would have to use a way to communicate these packets to the send_message method. One way is to use a queue: you can enqueue packets to it in this method, and dequeue them in send_message. You can also use the timeout argument of a queues dequeue method to implement timeouts in this assignment. You should immediately return from this method and not block.
ReliableMessageReceiver:
This class reliably receives a message from a sender. You have to implement the on_packet_received method. You can use self.send(packet) to send a packet back to the sender, and will have to call self.on_message_completed(message) when the complete message is received. Method descriptions:
__init__(self, ...) This is the constructor of the class where you can define any class attributes to maintain state. You should immediately return from this function and not block.
on_packet_received(self, packet: str) This method is invoked whenever a packet is received from the sender. You have to inspect the packet and determine what to do. You should immediately return from this method and not block. You can either ignore the packet, or send a corresponding ACK packet back to the sender by calling self.send(packet). If you determine that the sender has completely sent the message, call self.on_message_completed(message) with the completed message as its argument. Details on the role of a reliable receiver are provided in the next section.
Implementation Instructions
3.1 Packets
Youll need four types of packets in this assignment: "start", "end", "data", and "ack". All packets will also have a sequence number and a checksum. If the packet is a data packet, it will also contain the message content.
The packet format you have to follow is: "|||" An example start packet would be: "start|4212||1947410104"
For the message "request_users_list", with a chunk size of 10 bytes, the data packets would be: "data|4213|request_us|2826007388" "data|4214|ers_list|993936753"
You can use util.make_packet(...) to make a packet. It accepts packet_type, seq_num, and msg_content as arguments and returns a packet in the correct format. The returned packet also contains the checksum. For example, to make your start packet you can use the helper function as shown: start_packet = util.make_packet("start", start_seq_num) To validate the checksum, you can pass your packet to util.validate_checksum(...), which returns true/false accordingly. You can also use util.parse_packet(...) to parse a packet into its individual components (packet type, sequence number, data and checksum).
Implementing Reliable Transport
A message transmission will start from a start packet, and end with an end packet. You will be sending packets using a sliding window mechanism. For every received packet, the receiver will send a cumulative ACK with the sequence number it expects to receive next.
Receivers logic: 1. When you receive a packet, validate its checksum and ignore it if it is corrupted. 2. Inspect the packet_type and sequence number. 3. If the packet type is "start", prepare to store incoming chunks of data in some data structure and send an ACK back to the sender with the received packets sequence number + 1. 4. If the packet type is "data", store it in an appropriate data type (if it is not a duplicate packet you already have stored), and send a corresponding cumulative ACK. (ACK with the sequence number for which all previous packets have been received). 5. If the packet type is "end", assemble all the stored chunks into a message, call self.on_message_received(message) with the completed message, and send an ACK with the received packets sequence number + 1.
Starter code:
UTIL.PY:
'''
This file contains basic utility functions that you can use.
'''
import binascii
MAX_NUM_CLIENTS = 10
TIME_OUT = 0.5 # 500ms
NUM_OF_RETRANSMISSIONS = 3
CHUNK_SIZE = 1400 # 1400 Bytes
def validate_checksum(message):
'''
Validates Checksum of a message and returns true/false
'''
try:
msg, checksum = message.rsplit('|', 1)
msg += '|'
return generate_checksum(msg.encode()) == checksum
except BaseException:
return False
def generate_checksum(message):
'''
Returns Checksum of the given message
'''
return str(binascii.crc32(message) & 0xffffffff)
def make_packet(pck_type="data", seqno=0, msg=""):
'''
This will add the packet header to your message.
The formats is `
pck_type can be data, ack, end, start
seqno is a packet sequence number (integer)
msg is the actual message string
'''
body = "%s|%d|%s|" % (pck_type, seqno, msg)
checksum = generate_checksum(body.encode())
packet = "%s%s" % (body, checksum)
return packet
def parse_packet(packet):
'''
This function will parse the packet in the same way it was made in the above function.
'''
pieces = packet.split('|')
pck_type, seqno = pieces[0:2]
checksum = pieces[-1]
data = '|'.join(pieces[2:-1])
return pck_type, seqno, data, checksum
def make_message(msg_type, msg_format, message=None):
'''
This function can be used to format your message according
to any one of the formats described in the documentation.
msg_type defines type like join, disconnect etc.
msg_format is either 1,2,3 or 4
msg is remaining.
'''
if msg_format == 2:
return "%s" % (msg_type)
if msg_format in [1, 3, 4]:
return "%s %s" % (msg_type, message)
return ""
Starter code for RelaibleMessageSender in ReliableTransport.py:
import util
from queue import Queue
from typing import List class
ReliableMessageSender:
def __init__(self, ..., window_size: int): # Initialize any class attributes here
self.window_size = window_size
self.send_queue = Queue()
self.ack_queue = Queue()
self.next_seq_num = 0
self.timeout = util.TIME_OUT
self.num_retransmissions = util.NUM_OF_RETRANSMISSIONS
def send_message(self, message: str):
# Send start packet
start_packet = util.make_packet("start", self.next_seq_num, message)
self.send(start_packet)
self.next_seq_num += 1 #
Divide message into chunks and send data packets
chunks = [message[i:i + util.CHUNK_SIZE] for i in range(0, len(message), util.CHUNK_SIZE)]
for chunk in chunks:
packet = util.make_packet("data", self.next_seq_num, chunk)
self.send_queue.enqueue(packet)
self.next_seq_num += 1 #
Send end packet
end_packet = util.make_packet("end", self.next_seq_num)
self.send(end_packet)
self.next_seq_num += 1 #
Wait for all ACKs to be received
while not self.send_queue.empty():
packet = self.send_queue.peek()
if self.ack_queue.empty() or self.ack_queue.peek() < packet.seq_num:
self.send(packet)
packet.num_retransmissions += 1
if packet.num_retransmissions > self.num_retransmissions:
# Packet has been retransmitted too many times, give up return
# Restart timer for the first packet in the queue
if packet.seq_num == self.send_queue.queue[0].seq_num:
self.timer_start = time.time()
elif self.ack_queue.peek() == packet.seq_num:
# Remove the packet from the queue and the ACK from the ACK queue
self.send_queue.dequeue()
self.ack_queue.dequeue()
def on_packet_received(self, packet: str):
packet_type, seq_num, msg_content, checksum = util.parse_packet(packet)
if not util.validate_checksum(packet):
# Ignore packet if checksum is invalid return
if packet_type == "ack": self.ack_queue.enqueue(seq_num)
else: # Ignore packet if it is
RELIABLE MESSAGE RECEIVER.PY IN RELIABLE TRANSPORT.PY: CODE THIS IN PYTHON ON THE BASIS OF STARTER CODE PROVIDED ABOVE
class ReliableMessageReceiver(MessageReceiver):
'''
This class reliably receives a message from a sender.
You have to implement the on_packet_received method.
You can use self.send(packet) to send a packet back to the sender, and will have to call self.on_message_completed(message) when the complete message is received.
You can add as many helper functions as you want.
'''
def __init__(self, sock: socket, sender_addr: Address, msg_id: int,
completed_message_q: Queue):
MessageReceiver.__init__(self, sock, sender_addr, msg_id,
completed_message_q)
'''
This is the constructor of the class where you can define any class attributes to maintain state.
You should immediately return from this function and not block.
'''
def on_packet_received(self, packet: str):
'''
TO BE IMPLEMENTED BY STUDENTS
This method is invoked whenever a packet is received from the sender.
You have to inspect the packet and determine what to do.
You should immediately return from this method and not block.
You can either ignore the packet, or send a corresponding ACK packet back to the sender by calling self.send(packet).
If you determine that the sender has completely sent the message, call self.on_message_completed(message) with the completed message as its argument.
Receivers logic:
1) When you receive a packet, validate its checksum and ignore it if it is corrupted.
2) Inspect the packet_type and sequence number.
3) If the packet type is "start", prepare to store incoming chunks of data in some data structure and send an ACK back to the sender with the received packets sequence number + 1.
4) If the packet type is "data", store it in an appropriate data type (if it is not a duplicate packet you already have stored), and send a corresponding cumulative ACK. (ACK with the sequence number for which all previous packets have been received).
5) If the packet type is "end", assemble all the stored chunks into a message, call self.on_message_received(message) with the completed message, and send an ACK with the received packets sequence number + 1.
'''
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
