确保通过Python的UDP套接字接收所有数据

0 投票
1 回答
2889 浏览
提问于 2025-04-18 17:44

可能相关的问题,看起来和我的情况接近,但我理解的情况并不一样:

问题

通过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,这样你就不用写太多代码了。原因如下:

  1. UDP是一种不可靠的协议,简单来说,就是你发出去的数据包不一定能被接收方收到。
  2. UDP不保证数据包的接收顺序。这是因为UDP的数据包可能通过不同的路径到达接收方(也就是计算机之间的跳转)。所以后发的数据包可能走了条更短的路,先到达接收方,而之前发的数据包反而晚到了。(比如“结束\n”这个数据包可能会在其他数据包之前到达)

而TCP则是可靠的,接收到的数据包顺序是有保证的,非常适合文件传输。

不过别担心,像Bear Share和Bit Torrents这样的文件共享应用虽然使用UDP,但你需要做一些额外的编码工作。

  1. 你需要实现一个确认协议,也就是说,你需要给每个发送给接收方的数据包分配一个唯一的ID,当接收方收到这个数据包后,应该发送一个确认包回给发送方,告诉他这个数据包收到了。
  2. 如果数据包丢失了,接收方没有确认(也就是没有收到确认包),你就得重新发送这个数据包(一次又一次),直到收到确认。
  3. 你可以通过不发送下一个数据包,直到收到上一个数据包的确认,来控制发送顺序。

撰写回答