简体   繁体   English

python可靠的UDP和数据包“忽略”功能,具有选择非阻塞功能

[英]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. 我是python的新手,目前正在使用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 我不想使用Twisted或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). 我将仅在服务器端构建丢弃模拟功能,它将是“传入数据包丢弃功能”(服务器将数据以随机概率从套接字保存到套接字的“虚拟”变量中,因此套接字无法读取,并且直到新数据包到来之前,进程才会停止in)和“传出ACK丢弃功能”(即使服务器接收到数据并将数据写入文件中,也不会发送ACK,也不会发送ACK,但客户端无法到达ACK)。 I have built the first one, but not ACK drop function yet. 我已经建立了第一个,但还没有ACK丢弃功能。

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. 但是,当有数据包丢失时,我希望客户端等待几秒钟,然后一次又一次发送具有相同数据的数据包,直到它从服务器接收到先前发送的数据包的ACK。 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. 是错误的- if rsk is s ,则属于else ,因此在rsk is f的条件下(即文件可读时)到达。

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 是错误的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

increments read_cnt . 增加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; 我建议丢弃freadable并假设该文件是可读的必要时,以及丢弃swritable与移动的文件名数据包的发送之前,沿着while s:循环,下降的忙等待和使用3在超时 select呼叫; then you could just send a packet again when the time-out is reached. 那么您可以在达到超时时间时再次发送数据包。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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