[英]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
。
我建議丟棄f
從readable
並假設該文件是可讀的必要時,以及丟棄s
從writable
與移動的文件名數據包的發送之前,沿着while s:
循環,下降的忙等待和使用3在超時 select
呼叫; 那么您可以在達到超時時間時再次發送數據包。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.