diff --git a/udp.py b/udp.py new file mode 100644 index 0000000000000000000000000000000000000000..d54f55f170d490c456e266cb4ac40afeb3e01517 --- /dev/null +++ b/udp.py @@ -0,0 +1,104 @@ +import asyncio, websockets, json, base64, time +from struct import pack + +def display_packet(message): + #get parts of the header and the payload to display. Convert header parts to int and decode payload from bytes + #get source port as int - identifies the sender's port + source_port = int.from_bytes(message[0:2], 'little') + #get destination port as int - indentifies the server's port + dest_port = int.from_bytes(message[2:4], 'little') + #get length of UDP message as int - includes header and payload (minimum amount of bytes is 8) + length = int.from_bytes(message[4:6], 'little') + #get checksum as int - used for error-checking for the header/payload + checksum = int.from_bytes(message[6:8], 'little') + #get payload from packet - decode it from utf-8 + payload = message[8:(length+8)].decode("utf-8") + #print (decoded) packet details and it's payload + print("Server Sent:", message, "\nDecoded Packet:\nSource Port:", source_port, "\nDest Port:", dest_port, "\nData Length:", length, "\nChecksum:", checksum, "\nPayload:", payload, "\n") + +async def udp(): + #connect to server with specified port + uri = "ws://localhost:5612" + async with websockets.connect(uri) as websocket: + #receive welcome packet from server + message = await recv_and_decode_packet(websocket) + #display welcome packet + display_packet(message) + #loop forever + while True: + #send packet to server with specified parameters + await send_packet(websocket, 0, 542, b'1111') + #receive the packet the server sent back + message = await recv_and_decode_packet(websocket) + #delay by one second + time.sleep(1) + #display the packet the server sent back + display_packet(message) + +async def send_packet(websocket, dest_port, source_port, message): + #calculate checksum of packet/payload + checksum_bytes = int(compute_checksum(source_port, dest_port, message)).to_bytes(2, 'little') + #convert destination port to bytes + dest_bytes = int(dest_port).to_bytes(2, 'little') + #convert source port to bytes + source_bytes = int(source_port).to_bytes(2, 'little') + #convert length of payload to bytes + length = int(len(message)).to_bytes(2, 'little') + #construct packet with information that is converted into bytes + packet = dest_bytes + source_bytes + length + checksum_bytes + message + + #encode the bytes of packet with base64 + packet = base64.encodebytes(packet) + + #send packet to server + await websocket.send(packet) + +async def recv_and_decode_packet(websocket): + #receive packer from server + packet = await websocket.recv() + #display encoded packet + print(f"Base64: {packet}") + #return decoded packet + return base64.b64decode(packet) + +def compute_checksum(source, dest, payload): + #convert source port to bytes + source_bytes = int(source).to_bytes(2, 'little') + #convert destination port to bytes + dest_bytes = int(dest).to_bytes(2, 'little') + #convert length of payload to bytes + length_bytes = int(len(payload)).to_bytes(2, 'little') + #set bytes as 0 + size = bytes(2) + #construct packet + packet = dest_bytes + source_bytes + length_bytes + size + payload + + #initialise checksum as 0 + checksum = 0 + #length of the packet + length = len(packet) + #convert length of packet to bytes + size = length.to_bytes(2, 'little') + + #pre-process data for when the size of the packet isn't even. + if (length % 2 != 0): + #increase the length of the packet by 1 + length += 1 + #pad packet with the byte '0' to make it even in length + packet += pack('!B', 0) + + #for every two bytes in the packet + for i in range(0,len(packet),2): + #add the two bytes together - bitshift first byte by 8 + w = ((packet[i]) << 8) + ((packet[i + 1])) + #add onto the total checksum (running sum) + checksum += w + + #bitshift checksum to 16 bits + checksum = (checksum >> 16) + (checksum & 0xFFFF) + #one's compliment of checksum + checksum = ~checksum & 0xFFFF + #return the checksum + return checksum + +asyncio.run(udp())