简体   繁体   中英

python Reliable UDP and packet 'ignore' function with select non-blocking

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()

The client's line

            else:  # When socket is not readable

is wrong - the else belongs to if rsk is s and thus is reached under the condition rsk is f , ie when the file is readable.

Then, the next line

                if read_cnt > response_cnt: # When there is any data that has been read from file but not sent yet

is wrong - read_cnt is at most equal to, but never greater than response_cnt , since only

            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

increments read_cnt .

I recommend dropping f from readable and assuming the file is readable whenever necessary, as well as dropping s from writable along with moving the sending of the file name packet before the while s: loop, dropping the busy waiting and using a timeout of 3 in the select call; then you could just send a packet again when the time-out is reached.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM