I'm a novice of python and currently working on both client and server side scripts using non-blocking call with select. This is to reliably send a file from client to server even when there is a packet loss. I don't want to use pre-existing modules like Twisted or Eventlet
I assume that there is no packet corrupt of delay, but only drop occurs in random manner. I'm going to build drop simulation function in server side only, and it will be 'incoming packet drop function (server save data to 'dummy' variable from socket with random probability, so socket is not readable and progress stops until new packet comes in)', and 'outgoing ACK drop function (even if the server received the data and wrote the data to the file, the ACK is not sent, or sent but not reached by client). I have built the first one, but not ACK drop function yet.
Until now, I could send a file when there is no packet drop. However, when there is a packet drop , I want my client side to wait for a few seconds and send a packet with same data again and again until it receives ACK of previously sent packet from the server. But when there is a packet drop simulated by server, client functions stop working. I think there is something wrong with my code and need advices. In addition to that, it will be my pleasure to take any feedback for best practice writing these kind of codes. Thank you.
Here are what I have done until now. Sorry for my horrible coding manner by the way :(
Client side
import socket
import sys
import select
import time
host = ''
port = 1238
buf = 9216 # Jumboframe
addr = (host, port)
p_cnt = 0
read_cnt = 0
response_cnt = 0
response = ('','')
data = ''
# Socket Creation, binding
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('', 9996))
# Receive file name from user
file_name = "lol.png" # raw_input("Enter the name of the file: ")
f = open(file_name, "rb")
readable = [s, f]
writable = [s]
prev_data = file_name
while s:
try:
rd, wt, er = select.select(readable, writable, [], 0)
# When readable
for rsk in rd:
if rsk is s: # When socket is readable (3. Receive ACK from packet)
if p_cnt > response_cnt: # If sent packets > received response
response = s.recvfrom(buf) # Receive response from server
response_cnt += 1 # Increase the response counter by 1
print response[0] # Printout the received response
else: # When socket is not readable
if read_cnt > response_cnt: # When there is any data that has been read from file but not sent yet
start_time = time.time() # Start checking time
while 1:
if time.time() - start_time == 3: # When 3 second has been passed since the start time
print "Seems packet has been dropped, resending " + str(p_cnt) + " packet"
#for wsk in wt:
# if wsk is s:
s.sendto(data, addr) # Send the packet again
break # And break
# When readable
for rsk in rd:
if rsk is f: # When file is readable (2. Read data from FILE)
if response_cnt > read_cnt: # If received response > file read
data = f.read(buf) # Read more file
read_cnt += 1 # And Increase the read counter by 1
if data: # If there is a data read from file in buffer,
# print how many times it read the file
#if p_cnt > response_cnt:
# prev_data = data
print "Reading file...(" + str(read_cnt) + ")"
else: # If there is no data in the buffer,
# print there is no more data to send and close fileIO
print "No more data"
# When writable
for wsk in wt:
if wsk is s: # If socket is writable (1. Send data to server)
# If there is no packet sent to the server before
if p_cnt == 0:
# This is the sending the first packet includes file name
print "Sending file name"
# Send the file name to the server
s.sendto(file_name, addr)
# Print we sent the file name to server
print "File name sent"
p_cnt += 1 # And increase sent packet counter by 1
else: # If it's not a first packet to send
# If reading counter is same as packet counter,
# which mean we received response for what we sent
if read_cnt == p_cnt:
# If there is a data to send to server in the buffer
if(data):
# Send that data to the server
s.sendto(data, addr)
# And print out we just sent the data
print "Sent " + str(p_cnt) + " packet"
# And increase the sent packet counter by 1
p_cnt += 1
else: # If there is no data to send
# Sending empty data to let server know EOF
print "Sending EOF"
s.sendto("EOF", addr)
#s.sendto(data, addr)
p_cnt += 1
if response[0] == "ACK EOF":
print "Transmission complete" # Print the status
break
except socket.error, msg:
print 'Error Code : ' + str(msg[0]) + ' - ' + msg[1]
sys.exit()
f.close()
s.close() # Close the socket
sys.exit()
Server side
import socket
import sys
import select
import random
host = ''
port = 1238
buf = 9216 # Jumboframe
p_cnt = 0
response_cnt = 0
write_cnt = 0
dummy = ''
p1 = 0.8 # Probability that the packet will be dropped
# Socket creation, binding
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host, port))
# Start waiting for the socket
print "Waiting for incoming"
data, addr = s.recvfrom(buf) # Blocking
# Received first packet that contains file name
print "Received File: ", data.strip()
p_cnt += 1
file_name = data.strip()
f = open(file_name, "wb")
readable = [s]
writable = [s, f]
while s:
try:
rd, wt, er = select.select(readable, writable, [], 0)
response = "ACK " + str(p_cnt)
# When readable
for rsk in rd:
if rsk is s: # When socket is readable (1. Receive data)
# If there's only one packet received and no file written
if p_cnt == 1 and write_cnt == 0:
##############################################################################
if int(random.random() >= p1) == 1:
# Incoming packet dropped - Make socket unreadable
dummy, addr = s.recvfrom(buf)
print "Incoming packet " + str(p_cnt+1) + " dropped"
##############################################################################
else:
# Read the received data from socket
data, addr = s.recvfrom(buf)
# Print how many packet has been received until now
print "Received " + str(p_cnt) + " packet"
# If it's not the first packet or data has been written before
else: # If the number of received packet = number of sent ACK
# responsed to all the received packets to the client
if p_cnt == response_cnt:
##############################################################################
if int(random.random() >= p1) == 1:
# Incoming packet dropped - Make socket unreadable
dummy, addr = s.recvfrom(buf)
print "Incoming packet " + str(p_cnt+1) + " dropped"
##############################################################################
else:
# Read more data from socket and save to buffer
data, addr = s.recvfrom(buf)
# And increase the received packet counter by 1
p_cnt += 1
if data: # If there is data to read from socket,
# Print out how many packet that we have received
print "Received " + str(p_cnt) + " packet"
else: # If there is no data to read from socket,
# Print out there is no more data to read
print "No more data, closing file"
# When writable
for wsk in wt:
# When socket is writable (3. Send ACK)
if wsk is s:
# If number of writing times > number of ACK sent
if write_cnt > response_cnt:
# Send ACK to client
s.sendto(response, addr)
# And increase number of ACK sent
response_cnt += 1
# When file is writable (2. Write on FILE)
if wsk is f:
# If number of packet received > number of file writing
if p_cnt > write_cnt:
# If there is data to write in the buffer
if data and data != "EOF":
# And if it's not the first packet(file name, which shouldn't be written in file)
if p_cnt != 1:
f.write(data) # Write buffer to the file
write_cnt += 1 # And increase write counter
# Print how many times the buffer has been writted
print "Wrote " + str(p_cnt) + " packet"
else: # If there is no data to write in the buffer
print "Sending ACK EOF"
s.sendto("ACK EOF", addr)
if data == "EOF":
print "File Downloaded" # Print out the file download completed
break
except socket.error, msg:
print 'Error Code : ' + str(msg[0]) + ' - ' + msg[1]
sys.exit()
f.close()
s.close() # Close the socket
sys.exit()