确保通过Python的UDP套接字接收所有数据
可能相关的问题,看起来和我的情况接近,但我理解的情况并不一样:
问题
通过UDP在本地循环发送的长文件逐行发送时,并没有完全发送成功。
长话短说
我有一个长文件,里面是一些行和换行符,这个文件和我将通过UDP从另一个程序接收到的内容是一样的。我要强调的是,发送数据包的程序只能通过UDP发送(没有其他选择),而且在发送时无法修改来处理确认请求等。
文件的样子是这样的(这是最后几行):
StimulusTime 56398
Signal(0,2) -79.5457
Signal(0,4) -81.7426
Signal(0,6) -83.9978
Signal(0,9) -63.3755
Signal(0,11) -15.6045
Signal(0,13) 31.1299
Signal(0,16) 75.7539
Signal(0,18) 98.301
Signal(0,20) 98.301
Signal(0,22) 48.4546
Signal(0,25) 3.73159
Signal(0,27) -49.9798
Signal(0,29) -77.8449
Signal(1,0) -22.0332
Signal(1,2) -60.6384
Signal(1,4) -98.0858
Signal(1,6) -86.4579
Signal(1,9) -68.9173
Signal(1,11) -31.5552
Signal(1,13) 35.2906
Signal(1,16) 77.0686
Signal(1,18) 96.3836
Signal(1,20) 95.7246
Signal(1,23) 25.6074
Signal(1,25) -20.2101
Signal(1,27) -60.2933
Signal(1,29) -83.8169
Signal(2,0) -31.8826
Signal(2,2) -53.5045
Signal(2,4) -89.9895
Signal(2,7) -84.4503
Signal(2,9) -59.7016
Signal(2,11) -12.8569
Signal(2,13) 28.857
Signal(2,15) 58.0577
Signal(2,18) 96.4222
Signal(2,20) 79.783
Signal(2,22) 58.6463
Signal(2,25) -3.24883
Signal(2,27) -45.5
Signal(2,29) -88.8937
Signal(3,0) -18.6625
Signal(3,2) -53.3978
Signal(1,16) 58.784
Signal(1,17) 44.7782
Signal(1,18) 6.247
Signal(1,19) -12.0855
Signal(1,20) -33.7644
Signal(1,21) -49.4406
Signal(1,22) -67.5791
Signal(1,23) -92.0336
Signal(1,24) -93.9841
END
我写了代码,把这个文件逐行通过UDP发送,然后又写了代码来接收这些数据并根据数据类型进行解析。
发送者:
import socket
import sys
# Sends udp test data piped in from STDIN to the listener.
# ex: cat sampleoutput.txt | python testsender.py
UDP_IP = "127.0.0.1"
UDP_PORT = 5000
print "UDP target IP:", UDP_IP
print "UDP target port:", UDP_PORT
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
# Send from stdin
if len(sys.argv) < 2:
while True:
line = sys.stdin.readline()
if line:
sock.sendto(line, (UDP_IP, UDP_PORT))
else:
break
# get from file arg
else:
myfile = open(str(sys.argv[1]), "r")
while True:
line = myfile.readline()
if line:
sock.sendto(line, (UDP_IP, UDP_PORT))
else:
break
sock.close()
接收者:
import socket
from array import array
UDP_IP = "127.0.0.1"
UDP_PORT = 5000
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
while True:
data, addr = sock.recvfrom(1024) # buffer size arg
print data
# write to file for later testing
# file = open("testdummy.txt", "a")
# file.write(data)
if data == "END\n":
break
我用上面的接收者成功生成了原始程序的测试文件,所以应该是可以工作的。不幸的是,经过测试后,在发送大约500行数据时就失败了,虽然这个失败有点随机。具体来说,接收者没有在发送者退出之前接收到所有发送的行,导致它一直在等待 "END\n"
这个字符串。
据我了解,套接字已经处于阻塞模式——我该如何防止这种情况发生呢?
1 个回答
1
我的第一个建议是,如果你想传输文件并保持行的顺序,别用UDP。用TCP,这样你就不用写太多代码了。原因如下:
- UDP是一种不可靠的协议,简单来说,就是你发出去的数据包不一定能被接收方收到。
- UDP不保证数据包的接收顺序。这是因为UDP的数据包可能通过不同的路径到达接收方(也就是计算机之间的跳转)。所以后发的数据包可能走了条更短的路,先到达接收方,而之前发的数据包反而晚到了。(比如“结束\n”这个数据包可能会在其他数据包之前到达)
而TCP则是可靠的,接收到的数据包顺序是有保证的,非常适合文件传输。
不过别担心,像Bear Share和Bit Torrents这样的文件共享应用虽然使用UDP,但你需要做一些额外的编码工作。
- 你需要实现一个确认协议,也就是说,你需要给每个发送给接收方的数据包分配一个唯一的ID,当接收方收到这个数据包后,应该发送一个确认包回给发送方,告诉他这个数据包收到了。
- 如果数据包丢失了,接收方没有确认(也就是没有收到确认包),你就得重新发送这个数据包(一次又一次),直到收到确认。
- 你可以通过不发送下一个数据包,直到收到上一个数据包的确认,来控制发送顺序。