使用UDP在局域网内传输文件及相关问题

Zss 发表于:

UDP作为一个不可靠传输,当数据量大频繁的情况下,没有ack的确认,最后的文件很有可能打不开,可能文件在传输过程中丢失了数据

协议名为用户数据报文,这想着也不是用传一些大文件的,主要用来传输一些消息信息什么的还是不错,大文件还是用TCP吧,只是为了熟悉一下发包和数据的接收

socket中最大的接收为65535字节,用UDP协议发送时,用sendto函数最大能发送数据的长度为:65535- IP头(20) – UDP头(8)=65507字节。

用sendto函数发送数据时,如果发送数据长度大于该值,则函数会返回错误,以太网中的最大数据帧为:1518,刨去以太网帧的帧头(DMAC目的MAC地址48bits=6Bytes+SMAC源MAC地址48bits=6Bytes+Type域2Bytes)

14Bytes和帧尾CRC校验部分4Bytes那么剩下承载上层协议的地方也就是Data域最大就只能有1500Bytes,所以MTU值为1500,最大传输单元

UDP 包的大小就应该是 1500 – IP头(20) – UDP头(8) = 1472(Bytes)
TCP 包的大小就应该是 1500 – IP头(20) – TCP头(20) = 1460 (Bytes)

但是我在发送数据是抓包看到的是可以大于这个值很多,不清楚是为什么,不是不能大于传输单元吗?而且为什么数据没有被分片呢?

实际上是已经分片了,在udp中显示为60000的数据大小,但是在传输过程中抓包可以看到已经分片,如图

为什么不是1518呢?

实际上我们抓包得到的最大帧是1514字节,为什么不是 1518字节呢?原因是当数据帧到达网卡时,在物理层上网卡要先去掉前导同步码和帧开始定界符,然后对帧进行CRC检验,如果帧校验和错,就丢弃此帧。如 果校验和正确,就判断帧的目的硬件地址是否符合自己的接收条件(目的地址是自己的物理硬件地址、广播地址、可接收的多播硬件地址等),如果符合,就将帧交 “设备驱动程序”做进一步处理。这时我们的抓包软件才能抓到数据,因此,抓包软件抓到的是去掉前导同步码、帧开始分界符、FCS之外的数据,其最大值是 6+6+2+1500=1514。

测试了每个包以60000的数据量,和1472的数据大小发送,如下面所说分片次数越多越容易造成数据错误,也就是UDP当数据错误时,不会发生重传,那么文件就错误了

当以1472来传送时,数据不发发生分片,每次发送都有接收端的确认,所以传输变得可靠,当然传输速度降低了

相同的视频文件传过来变成了两个大小,且差别很大

如果我们定义的TCP和UDP包没有超过范围,那么我们的包在IP层就不用分包了,这样传输过程中就避免了在IP层组包发生的错误;

如果超过范围,既IP数据报大于1500字节,发送方IP层就需要将数据包分成若干片,而接收方IP层就需要进行数据报的重组。

更严重的是,如果使用UDP协议,当IP层组包发生错误,那么包就会被丢弃。接收方无法重组数据报,将导致丢弃整个IP数据报。

UDP不保证可靠传输;但是TCP发生组包错误时,该包会被重传,保证可靠传输

在传输的过程中,发现如果在发送端数据速度过快,不做延时,在接收端很有可能将数据拼接错误导致文件无法打开,所以在使用udp传输的时候,应该对传输速度是有限制的

在10M每秒的传输中,接收端的数据很容易错误,尝试添加延时,将速度限制在1M每秒后,数据传输正常,测试结果为越小的文件可靠性越高,

在我使用10M的文件来传输,1M的速度也会产生错误,当再次降低为500K速率来传输,数据再次正常

我想这也就是tftp为什么叫小文件传输了吧。。。使用的也是udp数据包来传输,而且还是有ack确认的

目的主要想熟悉一下使用socket对UDP的数据包的发送,之后再看看TCP是怎么来传输的,一个文件过大时使用分片的方式来分割所读取的二进制文件,依次发送在依次写入到文件中

既然UDP自身是不可靠的,测试中传输多次出现问题,那么我们可以自己来让他变得可靠,解决的办法是每当接收端收到一个数据包后返回一个确认的数据给发送端,这时候发送端才允许发送下一个数据包,

这样就解决了数据错位丢失的问题,但是带来的就是传输速度的限制,发送端不做任何的延时,在局域网内实测的速度1.5M/s每秒左右吧,大约带宽为12Mbps左右

发送端:

#coding:utf-8
from socket import *
import time

def burst_data():
    file_b = []
    with open('111.mp4','rb+') as f:
        data = f.read()
    index = int((len(data)/(float(1472))))
    for i in range(0,index+1):
        file_b.append(data[1472*i:1472*(i+1)])
    return file_b


if __name__ == '__main__':
    data_list = burst_data()
    udpsocket = socket(AF_INET,SOCK_DGRAM)
    udpsocket.bind(('',6666))
    t = 1
    for i in data_list:
        udpsocket.sendto(i,('192.168.16.116',7777))
        data = udpsocket.recvfrom(1024)
        if data[0] == 'pass':
            print('总数为:{}  已传输:{}'.format((len(data_list)),t))
        else:
            print('---------------数据丢失-------------')
        t += 1

接收端:

from socket import *

def get_data():
    udpsocket = socket(AF_INET,SOCK_DGRAM)
    udpsocket.bind(('',7777))
    while 1:
        data = udpsocket.recvfrom(1472)
        #print(len(data[0]))
        with open('111.mp4','ab+') as f:
            if len(data[0])==1472:
                f.write(data[0])
                udpsocket.sendto(b'pass',data[1])
            else:
                f.write(data[0])
                udpsocket.sendto(b'pass',data[1])
                break
if __name__ == '__main__':
    get_data()