Sfoglia il codice sorgente

added first try for a2. a day late. yay

Tareef 6 anni fa
parent
commit
13dd25a43f
5 ha cambiato i file con 225 aggiunte e 0 eliminazioni
  1. 0 0
      a2/3input.txt
  2. BIN
      a2/nEmulator-linux386
  3. 47 0
      a2/packet.py
  4. 58 0
      a2/receiver.py
  5. 120 0
      a2/sender.py

File diff suppressed because it is too large
+ 0 - 0
a2/3input.txt


BIN
a2/nEmulator-linux386


+ 47 - 0
a2/packet.py

@@ -0,0 +1,47 @@
+class packet:
+    MAX_DATA_LENGTH = 500
+    SEQ_NUM_MODULO = 32
+
+    # type is 0=ACK, 1=data, 2=EOT
+    # seq_num is the sequence number (mod 32)
+    # data is the string which is the data (empty if no data)
+    def __init__(self, type, seq_num, data):
+        if len(data) > self.MAX_DATA_LENGTH:
+            raise Exception("Data too large (max 500 char): ", len(data))
+
+        self.type = type
+        self.seq_num = seq_num % self.SEQ_NUM_MODULO
+        self.data = data
+
+    def get_udp_data(self):
+        array = bytearray()
+        array.extend(self.type.to_bytes(length=4, byteorder="big"))
+        array.extend(self.seq_num.to_bytes(length=4, byteorder="big"))
+        array.extend(len(self.data).to_bytes(length=4, byteorder="big"))
+        array.extend(self.data.encode())
+        return array
+
+    @staticmethod
+    def create_ack(seq_num):
+        return packet(0, seq_num, "")
+
+    @staticmethod
+    def create_packet(seq_num, data):
+        return packet(1, seq_num, data)
+
+    @staticmethod
+    def create_eot(seq_num):
+        return packet(2, seq_num, "")
+
+    @staticmethod
+    def parse_udp_data(UDPdata):
+        type = int.from_bytes(UDPdata[0:4], byteorder="big")
+        seq_num = int.from_bytes(UDPdata[4:8], byteorder="big")
+        length = int.from_bytes(UDPdata[8:12], byteorder="big")
+        if type == 0:
+            return packet.create_ack(seq_num)
+        elif type == 2:
+            return packet.create_eot(seq_num)
+        else:
+            UDPdata = UDPdata[12:12 + length].decode()
+            return packet(type, seq_num, UDPdata)

+ 58 - 0
a2/receiver.py

@@ -0,0 +1,58 @@
+from . import packet
+import socket, sys
+
+# save values needed to talk to host emulator
+haddr = sys.argv[1] # network host address
+dport = sys.argv[2] # dest port on host
+rport = sys.argv[3] # recv port for this app
+received = sys.argv[4] # filename to be used to record recvd data
+
+# try opening the file
+try:
+    msgfile = open(received, 'a')
+except IOError:
+    sys.stderr.write("Failed to open file to write data. Stop being terrible at life pls")
+    raise SystemExit
+
+# logfile (received packets)
+# at end call things.close()
+arrlog = open("arrival.log", "a")
+
+# some vars needed for execution
+expected = 0 # next packet # expected
+confirmed = None # last confirmed packet
+sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket!
+sock.bind(('localhost', rport)) # set socket to recv on rport
+pack_size = 512 # packet size in bytes
+
+# let's get this bread! I mean packets. yes, I re-used that joke.
+while(True):
+    pack, addr = sock.recvfrom(pack_size)
+    
+    # if we can't get a packet
+    if (not pack):
+        break
+    else:
+        packet = packet.parse_udp_data(pack)
+        
+    snum = packet.seq_num
+    arrlog.write(str(snum))
+    
+    if (snum == expected): # got the next packet
+        if (packet.type == 2): # EOT packet
+            # send EOT and exit
+            sock.sendto(packet.create_eot(snum).get_udp_data(), (haddr, dport))
+            break
+        elif (packet.type == 1): # data packet
+            # send ACK, record snum, increment expected
+            sock.sendto(packet.create_ack(snum).get_udp_data(), (haddr, dport))
+            confirmed = snum
+            expected = confirmed + 1
+            # deal with new data
+            msgfile.write(packet.data)
+            
+    elif (confirmed): # got wrong packet, send confirmation only of last good packet
+        sock.sendto(packet.create_acl(confirmed).get_udp_data(), (haddr, dport))
+        
+
+arrlog.close()

+ 120 - 0
a2/sender.py

@@ -0,0 +1,120 @@
+from . import packet
+import socket, sys, os, threading
+
+# save values needed to talk to host emulator
+haddr = sys.argv[1] # network host address
+dport = sys.argv[2] # dest port on host
+rport = sys.argv[3] # recv port for this app
+msg = sys.argv[4] # filename to be sent
+
+# logfiles (segnums and acks, respectively)
+# at end call things.close()
+seglog = open("segnum.log", "a")
+acklog = open("ack.log", "a")
+
+# some config vars
+winsize = 10 # window size
+tmout = 0.2 # tmout limit in s
+lock = threading.Lock() # used to avoid thread synch issues
+cv = threading.Condition(lock) # used to let our threads sleep when not needed
+timer = None # will be used for timing packet sendtimes
+first = 0 # first packet in the window
+confirmed = 0 # total confirmed packets for the current file
+snum = 0 # current number we want to send (sequence number, that is)
+char_limit = 500 # max num of characters in one packet's data field
+packets = [] # list to be turned into a list of packets
+total_packets = 0 # total number of packets
+timed_packet = -1 # packet timer corresponds to currently (-1 is a cardinal value)
+waking = False # flag if the receiver is waking the sender thread
+pack_size = 512 # packet size in bytes
+
+# try opening the file
+try:
+    msgfile = open(msg, 'r')
+except IOError:
+    sys.stderr.write("Failed to open file. Stop being terrible at life pls")
+    raise SystemExit
+
+# create packets list
+while(True):
+    string = msgfile.read(char_limit)
+    if (not string):
+        break
+    packets.append(packet.create_packet(total_packets, string))
+    total_packets += 1
+    
+# make winsize not bigger than the file allows
+winsize = min(winsize, total_packets)
+
+# create socket
+sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+sock.bind(('localhost', rport))
+
+# start receiver thread
+# receiver function
+def receiver():
+    print("YAY!\n")
+    
+    while(True):
+        # get a packet, turn into packet type
+        pack, addr = sock.recvfrom(pack_size)
+        packet = packet.parse_udp_data(pack)
+        
+        lock.acquire()
+        # what type is this?
+        if (packet.type == 2): # EOT
+            acklog.write(str(packet.seq_num))
+            lock.release()
+            return
+        elif (packet.type == 1): # data
+            lock.release()
+            sys.stderr.write("Got data from receiver. Exiting")
+            raise SystemExit
+        else: # ACK packet
+            acklog.write(str(packet.seq_num))
+            if (packet.seq_num > confirmed): # new ACK
+                confirmed = seq_num + 1
+                waking = True
+                cv.notify_all()
+            
+        
+
+recthread = threading.Thread(target=receiver, args=())
+#recthread.dameon = True
+recthread.start()
+    
+# let's get this bread! I mean send some packets
+while (confirmed < total_packets):
+    lock.acquire()
+    
+    # while we have room for packets in the window, send some
+    while(snum < confirmed + winsize and snum < total_packets):
+        sock.sendto(packets[snum].get_udp_data(), (haddr, dport))
+        seglog.write(str(snum))
+        snum += 1
+        
+    # use cv to sleep for 0.2s, or if woken up
+    cv.wait(tmout) # we get lock back when this returns
+    
+    # check if we woke from timer or not (status of waking flag)
+    # this means we were woken by an ACK.
+    if (waking):
+        # logic to deal with ACKS deal with in receiver
+        waking = False
+    else: # we need to reset window
+        snum = confirmed + 1 # reset to first unconfirmed packet
+        waking = False
+        
+    # ensure we don't overflow due to window reaching the end
+    winsize = min(winsize, total_packets - confirmed)
+    lock.release()
+
+
+# send EOT to receiver before closing
+sock.sendto(packet.create_eot(snum).get_udp_data(), (haddr, dport))
+recthread.join()
+sock.close()
+
+# cleanup for testing
+seglog.close()
+acklog.close()

Some files were not shown because too many files changed in this diff