[Python] UDP 전송 구현
2023. 10. 19. 03:23ㆍ네트워크
728x90
반응형
UDP 전송을 파이썬으로 구현해보았다.
Requester.py(Client)
"""
Name: Changjae Han
Date: Oct 10 2023
Email: chan82@wisc.edu
cslogin: changjae
File: requester.py
"""
import socket
import struct
import argparse
import datetime as dt
import time
"""Request_data
1. Create the requester and the sender socket
2. Pack the packet
3. Send it to the sender
"""
def request_data(req_port, file_option, row, is_first):
#Create a requester socket for UDP transmission
requester_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
requester_ip_addr = socket.gethostbyname(socket.gethostname())
requester_addr_port = (requester_ip_addr, req_port)
#Bind the socket to the requester's address
requester_socket.bind(requester_addr_port)
#Create a sender socket for UDP transmission
sender_ip_addr = socket.gethostbyname(socket.gethostname())
sender_addr_port = (sender_ip_addr, int(row[3]))
#Create packet header
header_info = struct.pack("!cII", b'R', 0, 0)
#Combine packet header with payload
packet = header_info + row[0].encode()
#Send the request packet to the sender
requester_socket.sendto(packet, sender_addr_port)
#Processing received data
process_data(file_option, is_first, requester_socket)
"""Process_data
1. Process the received data by unpacking
2. Calculate time and bytes
3. Write the data to the file
"""
def process_data(file_option, is_first, requester_socket):
sum_bytes = 0
sum_packets = 0
ptype = {b'D':"DATA", b'E':"END"}
#Check if it's first data
if is_first:
file = open(file_option, "w")
else:
file = open(file_option, "a")
#When the requester gets data from the sender
while True:
check_time = time.time()
received_packet, (sender_ip, sender_port) = requester_socket.recvfrom(1024)
wait_time = time.time()
packet_time = round(wait_time - check_time)
#Classify header and payload
received_header = received_packet[:9]
received_payload = received_packet[9:]
unpacked_header = struct.unpack("!cII", received_header)
#Print out packet information received
print(ptype[unpacked_header[0]] + " Packet")
print("recv time: ", dt.datetime.now(), sep='')
print("sender addr: ", sender_ip, ":", sender_port, sep='')
print("sequence: ", socket.ntohl(unpacked_header[1]))
print("length: ", unpacked_header[2])
sum_bytes += unpacked_header[2]
#If packet type is DATA
if unpacked_header[0] == b'D':
print("payload: " + str(received_payload[0:4].decode()))
print("")
sum_packets += 1
file.write(received_payload.decode())
else:#END
print("payload: 0")
print("")
print("Summary")
print("sender addr: ", sender_ip, ":", sender_port, sep='')
print("Total Data packets: ", sum_packets, sep='')
print("Total Data bytes: ", sum_bytes, sep='')
print("Average packets/second: ", packet_time, sep='')
break
file.close()
def main():
parser = argparse.ArgumentParser(description="requester data")
parser.add_argument('-p', type=int, metavar='port', help= 'requester port num')
parser.add_argument('-o', metavar='file_option', help = 'filename')
args = parser.parse_args()
is_first = True
tracker_file = open("tracker.txt", "r")
#Tracket_list to handle the order and unrelavant files
tracker_list = []
while True:
cur_line = tracker_file.readline()
if not cur_line: break
each_data = cur_line.split(" ")
each_data[-1] = each_data[-1].strip() #Remove each space in the end of cur_line
tracker_list.append(each_data)
#Sorted by num not to find the order over the list
tracker_list = sorted(tracker_list, key=lambda t: t[1])
print("Requester's print information")
print("-----------------------------------------------------------------------------")
for row in tracker_list:
if row[0] == args.o:#if it is requested file
startTime = time.perf_counter()
request_data(args.p, args.o, row, is_first)
endTime = time.perf_counter()
print("Duration of the test: ", round((endTime-startTime)*1000), " ms", sep='')
print("")
is_first = False
print("")
print("-----------------------------------------------------------------------------")
print("In addition, a file ", args.o, " will be generated in the directory requester/.", sep='')
print("The content should be:")
print("")
with open(args.o, encoding="UTF8") as data :
contents = data.read()
print(contents)
print("")
print("-----------------------")
if __name__ == "__main__":
main()
Sender.py(Server)
"""
Name: Changjae Han
Date: Oct 10 2023
Email: chan82@wisc.edu
cslogin: changjae
File: sender.py
"""
import argparse
import socket
import os
import struct
import datetime as dt
import time
"""Read_data
1. Create the sender socket
2. Read requested data by unpacking
3. Pass on to send_data
"""
def read_data(send_port, req_port, rate, seq_no, length):
#Create a sender socket for UDP transmission
sender_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
sender_ip_addr = socket.gethostbyname(socket.gethostname())
sender_addr_port = (sender_ip_addr, send_port)
#Bind the socket to the sender's address
sender_socket.bind(sender_addr_port)
print("UDP server is waiting for messages..")
#When the sender gets the request from the requester
while True:
data_received, requester_ip_addr = sender_socket.recvfrom(1024)
if not data_received: break #if requester disconnected
#Classify header and filename
header = data_received[:9]
decoded_header = struct.unpack("!cII", header)
filename = data_received[9:]
#If it is the requested packet
if decoded_header[0] == b'R':
send_data(filename, rate, seq_no, length, requester_ip_addr, sender_socket)
"""Send_data
1. Open file and send DATA packet to the requester
2. Also send the END packet to the requester after sending all packets
"""
def send_data(filename, rate, seq_no, length, requester_ip_addr, sender_socket):
requester_ip, requester_port = requester_ip_addr
print("-----------------------------------------------------------------------------")
print("sender's print information:")
#Read file
try:
file = open(filename, "r")
file_size = os.path.getsize(filename)
#Fail to read file
except FileNotFoundError:
print("It doesn't contain such a file")
pass
#Succeed to read file, send the data to the requester
else:
while (file_size > 0): #Decided by max_length, may not send the data all at once
if file_size > length:
data_length = length
else:
data_length = file_size
payload = file.read(data_length)
file_size -= length
#Pack and send the DATA packet to the requester
data_header = struct.pack("!cII", b'D', socket.htonl(seq_no), data_length)
packet = data_header + payload.encode()
sender_socket.sendto(packet, requester_ip_addr)
print("DATA Packet")
print("send time: ", dt.datetime.now(), sep='')
print("requester addr: ", requester_ip, ":", requester_port, sep='')
print("Sequence num: ", seq_no, sep='')
print("length: ", data_length, sep='')
print("payload: " + payload[0:4])
print("")
seq_no += data_length
time.sleep(1/rate) #Time delay by rate
file.close()
#Once it is done sending all data, send END packet to the requester
end_header = struct.pack("!cII", b'E', socket.htonl(seq_no), 0)
sender_socket.sendto(end_header, requester_ip_addr)
print("END Packet")
print("send time: ", dt.datetime.now(), sep='')
print("requester addr: ", requester_ip, ":", requester_port, sep='')
print("Sequence num: ", seq_no, sep='')
print("length: 0")
print("payload: ")
print("")
print("")
def main():
parser = argparse.ArgumentParser(description="sender data")
parser.add_argument('-p', type=int, metavar='port', help = 'sender port num')
parser.add_argument('-g', type=int, metavar='requester_port', help = 'requester port num')
parser.add_argument('-r', type=int, metavar='rate', help = 'packet rate per second')
parser.add_argument('-q', type=int, metavar='seq_no', help = 'sequence number')
parser.add_argument('-l', type=int, metavar='length', help = 'length of payload')
args = parser.parse_args()
read_data(args.p, args.g, args.r, args.q, args.l)
if __name__ == "__main__":
main()
구현 방법(리눅스):
한개 이상의 서버에 파일을 요청할 경우, 각 서버폴더에 있는 파일 이름과 순서, 호스트 네임과 포트번호를 Tracker.txt 파일에 적는다.
각각의 서버폴더에 파일과 sender.py를 복사한 후, Sender 와 Requester 폴더에서 다음과 같이 실행한다.
아웃풋 예시:
-----------------------------------------------------------------------------
sender1’s print information:
DATA Packet
send time: 2023-01-26 01:26:05.428
requester addr: 128.105.37.145:5001
Sequence num: 100
length: 23
payload: *Thi
END Packet
send time: 2023-01-26 01:26:06.429
requester addr: 128.105.37.145:5001
Sequence num: 123
length: 0
payload:
-----------------------------------------------------------------------------
sender2’s print information:
DATA Packet
send time: 2023-01-26 01:26:05.428
requester addr: 128.105.37.145:5001
Sequence num: 100
length: 20
payload: This
END Packet
send time: 2023-01-26 01:26:06.429
requester addr: 128.105.37.145:5001
Sequence num: 120
length: 0
payload:
-----------------------------------------------------------------------------
Requester’s print information:
DATA Packet
recv time: 2023-01-26 01:26:05.428
sender addr: 128.105.37.145:5000
sequence: 100
length: 23
payload: *Thi
DATA Packet
recv time: 2023-01-26 01:26:05.428
sender addr: 128.105.37.145:5002
sequence: 100
length: 20
payload: This
End Packet
recv time: 2023-01-26 01:26:06.429
sender addr: 128.105.37.145:5000
sequence: 123
length: 0
payload: 0
Summary
sender addr: 128.105.37.145:5000
Total Data packets: 1
Total Data bytes: 23
Average packets/second: 1
Duration of the test: 1001 ms
End Packet
recv time: 2023-01-26 01:26:06.429
sender addr: 128.105.37.145:5002
sequence: 120
length: 0
payload: 0
Summary
sender addr: 128.105.37.145:5002
Total Data packets: 1
Total Data bytes: 20
Average packets/second: 1
Duration of the test: 1001 ms
-----------------------------------------------------------------------------
In addition, a file split.txt will be generated in the directory requester/.
The content should be:
*This is split1.txt.
*
This is split2.txt.
-----------------------
그러나 실행시켜보면 위와 똑같은 UDP 통신 구현은 아직 되지 않은 것을 알 수 있다. Tracker_list에 있는 라인을 읽는 과정에서 동시적으로 파일을 request하지 않기 때문이다. (전송이 끝난 후 다음 전송) 멀티쓰레딩을 활용할 수도 있지만, 그렇지 않고도 구현할 수 있다고 하니, 이번에 파이썬으로 감을 잡았으니 다음엔 C++로 동시에 파일을 request하는 UDP를 구현해보겠다
반응형
'네트워크' 카테고리의 다른 글
이더넷(Ethernet)과 LAN Interconnection (0) | 2023.10.25 |
---|---|
프레이밍과 바이트지향 프로토콜 (1) | 2023.10.25 |
네트워크 퍼포먼스(Latency, Bandwidth) (0) | 2023.09.26 |
인터넷과 건축적 관점 (0) | 2023.09.22 |
패킷스위칭 네트워크의 방식 (0) | 2023.09.15 |