簡體   English   中英

python可靠的UDP和數據包“忽略”功能,具有選擇非阻塞功能

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

我是python的新手,目前正在使用select的非阻塞調用來處理客戶端和服務器端腳本。 即使有數據包丟失,這也可以可靠地將文件從客戶端發送到服務器。 我不想使用Twisted或Eventlet等預先存在的模塊

我假設沒有數據包延遲的破壞,但是只有丟棄是隨機發生的。 我將僅在服務器端構建丟棄模擬功能,它將是“傳入數據包丟棄功能”(服務器將數據以隨機概率從套接字保存到套接字的“虛擬”變量中,因此套接字無法讀取,並且直到新數據包到來之前,進程才會停止in)和“傳出ACK丟棄功能”(即使服務器接收到數據並將數據寫入文件中,也不會發送ACK,也不會發送ACK,但客戶端無法到達ACK)。 我已經建立了第一個,但還沒有ACK丟棄功能。

到現在為止,我可以在沒有數據包丟失的情況下發送文件。 但是,當有數據包丟失時,我希望客戶端等待幾秒鍾,然后一次又一次發送具有相同數據的數據包,直到它從服務器接收到先前發送的數據包的ACK。 但是,如果服務器模擬了丟包,則客戶端功能將停止工作。 我認為我的代碼有問題,需要建議。 除此之外,我很高興收到任何反饋,以獲取最佳實踐,以編寫此類代碼。 謝謝。

這是到目前為止我所做的。 對不起,我的編碼方式很糟糕:(

客戶端

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

服務器端

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

客戶專線

            else:  # When socket is not readable

是錯誤的- if rsk is s ,則屬於else ,因此在rsk is f的條件下(即文件可讀時)到達。

然后,下一行

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

是錯誤的read_cnt最多等於,但絕不大於response_cnt ,因為僅

            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

增加read_cnt

我建議丟棄freadable並假設該文件是可讀的必要時,以及丟棄swritable與移動的文件名數據包的發送之前,沿着while s:循環,下降的忙等待和使用3在超時 select呼叫; 那么您可以在達到超時時間時再次發送數據包。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM