python可靠的UDP和数据包'ignore'功能,选择非阻塞

时间:2015-11-21 02:50:35

标签: python sockets udp

我是python的新手,目前使用select的非阻塞调用来处理客户端和服务器端脚本。这是为了可靠地将文件从客户端发送到服务器,即使在丢包时也是如此。我不想使用预先存在的模块,如Twisted或Eventlet

我假设没有数据包损坏延迟,但只有丢弃以随机方式发生。我将仅在服务器端构建drop simulation功能,它将是'传入数据包丢弃功能(服务器将数据保存到来自套接字的'dummy'变量,具有随机概率,因此套接字不可读并且进度停止直到新数据包到来in)'和'传出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()

1 个答案:

答案 0 :(得分:0)

客户的行

            else:  # When socket is not readable

错误 - else属于if rsk is s,因此在rsk is f条件下达到,{i}即当文件可读时。

然后,下一行

                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:循环之前的数据包,在select呼叫中丢弃忙等待并使用超时 3;然后你可以在超时时再次发送数据包。