哈工大毕设记录-使用ZYNQ MPSoC开发板实现的Linux环境千兆以太网C语言UDP协议批量文件存取(上)

阅读须知

(为什么平台会自己设置VIP文章?我写出来就是给人看的VIP干什么?)

写在前面:

本文仅为一位哈工大本科学生的毕设过程记录(吐槽),可参考性有限,供后来的广大学弟学妹们参考一下吧,我趟过的坑别再跳了。

字体区别:

黑色加粗为文章结构脉络表述,红色为必须明确的重点,绿色为次重点,蓝色为吐槽。

上篇绪论

本文主要描述内容包括以下六条,分上下篇,123上篇,456下篇(下篇:“咕咕~有空再写~”):

  1. 如何使用AD迅速开展能够应对本科毕设等级的PCB绘画工作(不涉及制板);
  2. 如何利用Petalinux开发套件进行嵌入式系统移植,并进行后续的APP定制等操作;
  3. 如何编写Linux系统下使用C语言的UDP/IP协议文件传输程序,并实现多个文件在至指定路径之间的逐个传输;
  4. Linux系统下如何挂载SSD固态硬盘并设置开机自动挂载;
  5. 如何将开发板与虚拟机通过网线连接在一起,同时保证虚拟机与主机的正常上网;
  6. 如何在开发板上测量SSD的读写速度(这条或许有点问题,我理解的不彻底);

 一、如何迅速开展能够应对本科毕设等级的PCB绘画工作(不涉及实际制板)

其实我挺不想写这个开头段,跟论文每章前面的引言似的,但总得让人知道我在干嘛吧,水平有限,各位多多担待。

这章讲的是怎么快速开展PCB绘画工作,以及在整个PCB绘画过程中大概率会遇到的一些问题,怎么解决这些问题,以及一点注意事项。

第一步,粗略了解画PCB的流程和具体操作

扔出来三个视频链接,去看,总要先了解一下画PCB的流程和具体操作,心里有个大概概念,至于某些东西的操作为什么要这么做,只有开始画了才能想明白,尚且不提。

在此感谢B站这位素未谋面的吴老师“HD匠”,PCB设计流程扫盲如何快速阅读芯片手册使用AD16.1绘画PCB,这三个教程给了我很大的帮助,画着画着遇到问题再回去看看,有很多问题都能自行解决。

不过,更多的问题还是需要看AD的检查报错,真的得看懂报错,然后想想为什么会出现这个错误,这样问题才解决的快。比如我自己碰见的一个很傻的问题,PCB元件引入之后,连接器的所有引脚网络标识一直没有,和别的元件一条白线都拉不出来,报错写的是“某某元件找不到某某引脚”,最后发现是我自己建立原理图元件时候,把引脚的序号设置成和引脚名字一样的了,程序看着我设置的引脚“A1”能认出来是引脚1就怪了。笑死,这问题想两周才突然灵光一闪发现自己干了什么。

第二步,从功能出发,构建/理解原理图

我们现在知道了PCB绘画工作的流程,就要开展应付毕设等级的PCB绘画工作了,而在这个过程中,需要解决最先找上门的问题——原理图为什么这么画。因为是毕设嘛,选题肯定已知,要照着你任务书的功能和指标进行选件,先跟亲爱的指导老师一顿摸爬滚打选了元件,再照着教程人模狗样(不是)的看了元件手册,你才能知道原理图该怎么画,你想实现的功能到底要怎么构建电路。

不过,本科毕设总是会简单许多,实际的情况很可能是这样的(我就这样)。师兄/老师扔给你一份开发板资料,然后你就照着人家开发板的电路,把自己想要的功能部分一找,开始一顿狂画,结果就被原理图淹没、不知所措,好不容易把自己想要的原理图画完了,一回头师兄考你原理图为啥这么画,直接傻眼,因为虽然好好的建了元件库、看了元件手册,但看的是皮毛,只理解大体功能,没寻思电路具体某个部分为啥这么画,某个元件为啥这样取值。我直接:“坏了,被师兄发现我是铁fw了。”

针对这个问题,还是老样子,扔点儿可能遇见的问题视频链接,原理图里面0欧的电阻是什么意思上拉电阻的取值什么是阻抗匹配。另外,这里需要补充一些我遇到的,但没有视频讲解的问题,是从各个网站上搜集的答案,我的描述不一定准确,但大概是这个意思。当然,只看大概是不行的,彻底明白要自己去查每个词的意思,包括提到的名词也是,另外阻抗匹配可能本科确实做不出来,至少我不行,我光画PCB不制版,很多板材参数都没有,这里也就是意思意思,最后拿嘉立创的阻抗匹配神器算了一下线宽和间距,PCB照着设置一顿就当做出来了,真正制板的同学们得找会这东西的老师求助,想靠自己在毕设期间搞定制板实现,没那么简单(至少我现在这么认为,2023.6.10)。

以下是补充的可能遇到的问题:

NC电阻的作用:
预留位,不焊电阻,PCB布线时也要预留哦。

各种电容的作用:
相对大的电容防止浪涌;小电容滤高频干扰,电容越小,谐振频率越高,可滤除的干扰频率越高。

电源输入处共模滤波器的作用:
消除开关干扰。

GND_EARTH是防雷参考地,可以通过一些处理接电路地GND,具体操作参考一下这个链接,GND_EARTH和GND的关系是什么。各种地分不明白的可以看看这个,总说电路的“地”

PCB上面的SATA硬盘固定孔:
M.2接口的固态硬盘有四个标准,即2280、2260、2242和2230,意思是硬盘的宽度都是22mm,而长度分别为80、60、42和30mm,一种长度对应一个孔。

第三步,布线,绘制PCB

我们完成了原理图的绘制,开始绘制PCB,这个过程中,需要不断解决一个问题——元器件的分布与布线。当然,刚导入完原理图,看着一大堆元件横排一列,这个问题就会一个直拳打过来,当时打的我是头晕脑胀,不过现在回头看我有个大问题,就是没明确到底画几层板,层怎么分,一看别人的经验,“哎呀~分那么多层,好复杂,我学的会吗?”,然后犯懒画了个两层板,结果RTL8211FD-CG芯片直接让我布线到挠头,后面的等长处理更是做的吐血,我在这里奉劝各位同学,毕设PCB布线可与金工实习那简单电路不同,分个多层板很可能会减少很多难度。但!画几层板还是取决于时间宽裕与否和电路简单与否,这个问题要各位同学自己斟酌,从长远角度来看肯定是画多层板适合学习与后续发展的,但别就剩两周还硬挺着啥都干到最好,保命要紧,我当时犯懒了,给不出多少建议,百度/bing一下吧。

当然,画PCB的途中总是会遇到一些操作上的疑问,比如“哎呀~我不会做等长处理,怎么办呀~?”,那没办法,真的只能百度一下了,软件不同版本不同带来的操作差异是一个教程无法解决的,各位同学加油,搜一搜试着干个大半天,总是能干出来的。

最后,如果真的有同学本科就需要制出来板子,加油吧,一定要多和老师沟通,实际制板的要求还是挺高的。

啊哈!不用写本章小结真爽!

二、如何利用Petalinux开发套件进行嵌入式系统移植,并进行后续的APP定制等操作

嗯哼该死的开头段它又来了,又不爽了。

这章略讲一下我是怎么进行petalinux系统移植并且进行后续的APP定制等操作,但这东西大佬们都讲烂了,我就直接给一些我当时照着做的步骤和参考的链接,再描述一下我遇到的奇奇怪怪的问题。还有一些对陈年二手轮子的吐槽,先叠个甲,轮子问题可能是我水平不足才会碰见,还请各位大佬别打我。

第一步,照例,扔视频链接了解一下总体流程和操作方法

Petalinux基本搭建方法这个视频前半段14:40之前对Petalinux套件的基本搭建不论什么类型的板子都应该是一样的,从打开Vivado开始就各有各的方法了,视频里面看的两个专栏可以照着去搜,跟着做,都大差不差,顶大是AMD注册下载petalinux.run稍微卡你一下,然后注意虚拟机网要好一点,这样安的快。另外,同学跟我说petalinux有中文官方教程,我当时犯傻没看到,后来的同学可以去搜搜,看官方的还是保准的。(2024.4.1,才看见有人问能不能给个网址,我查了一下可能是这个,v2019.1的,或许差不多,PetaLinux工具文档参考指南UG1144(v2019.1)

第二步用vivado软件生成硬件环境,然后用生成的文件去建立petalinux系统,并且部署到SD卡里面,进行启动测试

这一步会有不同petalinux版本的差异、不同使用需求的差异、不同系统部署方式的差异,我只需要用mpsoc的ps端,其他乱七八糟的不用连,硬件环境拿xsa文件就够用,有的同学因为和我使用需求不同,可能得到后面vitisSDK里面生成的乱七八糟的板间级文件,并且我从SD卡启动系统,把SD卡分个区然后系统文件放里面就可以,其他的方式我不了解,这里不提。

我的工作步骤是:选好要用的型号的芯片,添加一个ZYNQ MPSoC的IP核,直接引用米联客MZU07A开发板的IP核设置(要从例程里面自己导出来一个设置文件,不要用他给的设置文件),去除不要的外设和PS-PL连接勾选,生成顶层文件,生成比特流文件,把生成的xsa文件复制到我自己创建的petalinux工程文件夹中,用petalinux命令生成petalinux系统,然后把系统文件复制到SD卡里面,开发板拨码设置从SD卡启动。

这里我把建立系统需要用到的各种petalinux命令整理了一下:

使用petalinux命令前的准备:
在安装好的petalinux路径下打开命令行,路径位置如下图所示,我是在这个peta文件夹里面安装的petalinux,输入指令:

source settings.sh

使用cd ..退出安装文件夹,然后到你想要的地方建立工程,我是整了个mpsoc文件夹装工程,具体操作可以参考刚刚链接的视频18:02之后的内容,Petalinux基本搭建方法

template选项的具体内容需要根据芯片型号不同进行修改:

petalinux-create --type project --template zynqMP --name petalinux_prj

把vivado生成的xsa文件复制到刚刚建立的工程文件夹里,路径位置如下图所示,放到文件夹里面就行,运行这行代码进行选中xsa文件:

petalinux-config --get-hw-description=.

进行任何设置修改都需要输入以下命令建立系统,包括更改app设置:

petalinux-build

想进行系统设置更改时,比如设置系统启动模式为SD卡或启动mkfs指令(我就改了这两个),就会需要以下两种命令配置系统内核设置,记得用这两条要把命令行界面最大化,窗口不够大会报错,各种具体用法具体分析,同学们按需求问问百度:

petalinux-config
petalinux-config -c rootfs

以下指令用于生成系统配置文件,生成文件的路径位置如图所示,即“你创建的工程文件夹/images/linux”,图中选中的三个文件用于SD卡移植,BOOT.BIN和image.ub复制到SD卡的BOOT分区,rootfs.tar.gz复制到SD卡的rootfs分区并解压即可:

petalinux-package --boot --fsbl --fpga --u-boot --force

第三步创建APP程序并部署至开发板

嵌入式系统已经有了,SD卡插上板子测试也正常运行,得搞一个运行的软件实现咱们想要的功能,这就需要创建自己的APP程序,再将这个程序与系统一同部署好,部署完了插进板子启动后,直接输入自己app程序的名字即可运行程序,比如我下文提到我自己写的那个myserver程序,板子开机成功启动后,在串口工具中直接输入myserver就能运行程序,以下是创建自己的APP程序需要用到的petalinux代码:

在创建的petalinux工程目录下创建你的app程序,并使能对你的应用程序的编译:

petalinux-create -t apps -n myserver --enable

在创建的petalinux工程目录下编译应用程序工程:

petalinux-build -c myserver -x do_install

创建好的app程序路径如下图所示,“你创建的工程文件夹/project-spec/meta-user/recipes-apps”,更改myserver.c内的代码,改成你要实现功能所用的代码即可。

确认更改的代码能够实现想要的功能后,需要将应用程序编译入系统镜像,运行以下两条指令,运行完成后需要重新输入petalinux-build建立系统并生成配置文件:

petalinux-build -c rootfs
petalinux-build -x package

另外提一个因为官方版本差异导致的问题,当使用petalinux2019.2时,如果你想和下文链接文章所提到的一样,zynq操作系统:iperf的安装和使用,开启一些petalinux已经自带源码,但没有在默认设置中设置成可以启用的软件,比如iperf2和iperf3,会因为官方petalinux2019.2的版本差异,发现链接文章步骤二、2.中提到的路径< plnx-proj-root >/project-spec/meta-user/recipes-core/images/petalinux-image.bbappend于你的同样路径中并不存在的问题,2019.2版本需要改的文件和改法如下图所示,即“你的工程文件夹/project-spec/meta-user/conf/user-rootfsconfig”,红框内的代码就是后添加的启用iperf2和iperf3,添加这两行后再进入rootfs设置更改界面就能找到iperf2和iperf3了,官方的文件也有提到过这个事情,但我现在找不到官方论坛的链接出处,这里就不放了,各位想找的自己看看。

第四步,更新嵌入式系统文件

我们想要的APP不管是自建的还是已有设置启动的都已经改好了,要更新SD卡里部署的系统,先得解除SD卡rootfs分区的管理员权限,并解压安装包。SD卡分区的方法其他教程讲过很多,搜一下就好,大同小异。解除权限需要运行的指令如下:
在SD卡的rootfs分区里打开命令行,先输入cd ..退出rootfs文件夹,再输入:

sudo chmod -R 777 rootfs/

此时rootfs文件夹的管理员权限就解除了,可以正常删除文件,删掉原来的系统文件。

将新的系统压缩包复制过来,再cd rootfs/重新进入文件夹,输入以下指令,以管理员权限解压刚刚复制到rootfs文件夹的系统压缩包rootfs.tar.gz  :

sudo tar -zxvf rootfs.tar.gz

如果你需要对生成的系统文件进行其他petalinux套件未涉及的系统设置更改,比如更改系统network文件重新设置以太网IP,或更改系统fstab文件增加开机挂载SSD,就要在rootfs文件夹里再次解除管理员权限限制,解除限制后可正常更改系统设置文件:

sudo chmod -R 777 *

到我心心念念的吐槽环节了。我这不是用的米联客的MZU07A开发板嘛,结果照着米联客2019的pdf第三章移植petalinux教程做,第一步就卡住。或许是我拿的教程版本太旧有问题,拿着米联客那个压缩包里面附带的环境文件,建好项目文件,把那行导入设置的source代码一跑,就会在C盘的默认路径下重新生成一个项目,好家伙跟他写的导入设置的效果也不一样啊。

当时直接给我干蒙了,不用source代码、换成导入设置文件也不好使,vivado显示该设置文件不可用,我甚至尝试了给盘改名再重新建立文件夹,就为了和教程的路径一模一样,但还是会重新生成一个项目,然后我自己建立的那个项目照样光秃秃,设置导入了个寂寞。

更别提后面那个更改设备树,都是改设备树的同一个文件,第三章教程的设备树文件路径和第二章教程的设备树文件路径居然不一样,照着第三章的改会导致后面系统生成跑不通,甚至最后搞出来的linux系统fstab文件是空的,照着第二章的改反而好使,我真是麻了。

到最后我想了想,反正也要拿米联客的开发板,他那个整体的项目文件也给了,干脆就不用他单独给的配置文件了,我自己再用vivado程序生成配置文件不就完了,于是从他例程zu_prj项目的IP核导出设置文件,自建一个新的vivado工程,用我自己重新导出的设置文件导入IP核设置,再去除不要的外设和PS-PL连接勾选,然后根据这个核心生成的xsa,用赛灵思官方的原版petalinux生成后面的系统文件,这么搞才成功。

哇写到这里好累,写了三个点,歇一会儿,跟室友打王者去。

三、如何编写Linux环境下C语言使用UDP/IP协议的批量文件存取程序

啊,好累,第二天(2023.6.11)接着写,好像又阳了,有点难受,唉,腰疼,分个上下篇吧,下篇这两天是彻底写不出来了,本来还想把整个经历在工大校园里面写完的,图个仪式感嘛。

笑死,第三天也没写完上篇(2023.6.12)。

这章大体讲怎么在Linux系统里面用C语言写基于以太网UDP/IP协议的批量文件传输,最终效果是通过以太网传输命令,在两个指定路径位置之间批量收发文件。这章会对sendto/recvfrom函数怎么用、待传输文件的生成过程、如何实现批量文件的逐个传输、如何实现较大文件的传输等停这四个主要问题进行简单讲解,最后给大伙简单讲一嘴我自己写出来的东西。

第一步,了解网络协议有关的知识是必须的

照例放出文章链接,慢慢的把东西都看全了,自己再补充一些基础知识,仔细想想,后续编程过程中的疑惑就会少很多很多,尤其是第二个链接关于socket部分的原理与流程讲解,很多网上的教程只告诉你程序怎么写,不会告诉你socket到底在数据传递的过程中担任了一个什么角色,以及操作系统在这个过程中起到了什么功能,不了解这个的话,对自己在干什么不可能有清楚的认知。另外,整体的编程结束之后,强烈建议再看一次第二篇链接文章里面的图,阅读的体会非常不一样,有开发板可用的同学更是这样。

TCP/IP详解学习笔记Linux操作系统原理—内核网络协议栈

第二步,练习基本的UDP收发代码编写

开始学着写最基本的UDP收发代码,能做到简单的收发文件,构建一个程序大框架即可。收发双端一个server服务端、一个client客户端,socket套接字的初始化过程以及数据传输的代码写法都大同小异,不同的区别仅在于需求不同导致的程序整体结构不同。这里给出六篇文章链接作为程序代码写法学习的参考,第一篇简单了解UDP收发的流程,第二、三篇文章学习UDP收发的基本代码写法,第四篇补充UDP收发函数sendto和recvfrom的定义,第五篇补充socket初始化过程中用到的两个结构体定义,第六篇是一个实例,有较为成熟的程序整体框架,比较适合用来借鉴构思自己的思路,并以此框架重构自己的框架,第六篇还有个有意思的结构体设置,我的数据缓冲区构建方式就是学第六篇文章的写法,将操作指令、待传输文件编号、读入数据长度、文件数据缓冲区等要传输的信息统合在一个结构体中,这样可以为信息在双端之间的交流与判断省很多事,只需要发一次数据、发一个结构体就解决一大堆需求。不过我看的时候第六篇还没要会员,这我就没办法了,我这边最后写出来的东西已经改的和他原本的面目全非了。

【C/C++套接字编程】UDP通信实验基于Linux用C语言实现TCP/UDP图片和文件传输(socket)Linux C udp实现文件传输UDP传输 :recvfrom 函数与 sendto 函数分析sockaddr和sockaddr_in详解Linux网络编程——UDP通信(文件传输)

阅读文章时需要明确两个事情。

第一点,第六篇链接文章里面recvfrom和sendto语句的收发对象设置好像有点儿问题,不要学他的写法,学前两个链接文章的收发语句怎么写,看第六篇写的收发语句会越看越乱。第二点,这些文章的UDP传输代码都很难传输比较大的文件,比如我毕设就得传几个G的文件,但毕竟是UDP传输,真要求数据严格没错为啥不用TCP,所以我自己编的程序也只是加个数据读写的等停,数据重传根本就没写,构思还挺不成熟的。

当然,我话还没说完,怎么可能把问题全都交给其他文章呢~,我可不喜欢不负责~这里扔两个核心问题的解答,剩下的就同学们自行百度吧~。

问题一:UDP收发函数sendto和recvfrom的用法

先简单几句话解释解释UDP收发函数sendto和recvfrom的用法吧,说点儿大白话(笑)。

sendto函数的六个输入定义用大白话说就是:

sendto(    int 你要用的套接字描述符(使用socket函数初始化套接字时接收返回值的那个变量),

                const void *你想发出去的数据缓冲区地址  ,

                int 你想发出去的数据长度(经常为缓冲区的长度,即sizeof(缓冲区)),

                unsigned int 发送方式(经常为0,其余的发送方式有需求自行查找),

                const struct sockaddr *你想发给哪个地址(因地址初始化时为sockaddr_in结构体,所以如果编译时想没有warning,这一项的前面就会带有强制转换,即有(struct sockaddr *)的存在)  ,

                int 你想发给的地址的地址结构体长度(即sizeof(第五个输入的地址结构体长度)));

举个sendto例子

想让服务端使用套接字sockfd、发送数据缓冲区pro保存的所有数据、发给客户端client,写出来的代码就是:

sendto( sockfd  ,  &pro ,  sizeof(pro)  ,  0  ,  (struct sockaddr *)&client  ,  sizeof(client)  );

recvfrom函数的六个输入定义用大白话说就是:

recvfrom(    int 你要用的套接字描述符(使用socket函数初始化套接字时接收返回值的那个变量),

                   void *你想存放接收数据的缓冲区地址  ,

                   int 你想接收的数据长度(经常为缓冲区的长度,即sizeof(缓冲区)),

                   unsigned int 发送方式(经常为0,其余的发送方式有需求自行查找),

                   struct sockaddr *数据发送方的地址(因地址初始化时为sockaddr_in结构体,所以如果编译时想没有warning,这一项的前面就会带有强制转换,即有(struct sockaddr *)的存在)  ,

                   int 数据发送方的地址的地址结构体长度(即sizeof(第五个输入的地址结构体长度)));

举个recvfrom例子

想让客户端使用套接字sockfd、使用数据缓冲区pro保存接收到的数据直到缓冲区pro存满、接收服务端server发送的数据,写出来的代码就是:

recvfrom( sockfd  ,  &pro ,  sizeof(pro)  ,  0  ,  (struct sockaddr *)&server  ,  sizeof(server)  );

问题二:目标文件的数据从何而来

我们已经知道,既然sendto和recvfrom实现的是数据在双端缓冲区之间的收发,那目标文件的数据是怎么读进发送端的缓冲区的?同样,接收端缓冲区里面的数据又是怎么变成接收端目标文件的?

结合收发代码的写法和背景知识的第二篇链接文章,现在我们知道了数据到底是怎么成功收发到双端的缓冲区里面的,但仔细思考过后,就会发现两个问题,那就是:“文件数据是怎么进到发送那边的缓冲区里面的,sendto函数管的不是这个啊?文件数据又是怎么在接收那边的缓冲区跑到传输生成的目标文件里面的,recvfrom函数管的也不是这个啊?”

这就要再看回到代码里面了,给出的文章2、3里面不管是服务端还是客户端,都有一个FILE *fp指针的身影,就是通过fopen、fread、fwrite这三个函数对fp文件指针的操作,实现了对文件数据的读和写,这里需要补充一些关于FILE以及三个函数具体用法的知识,Linux下 文件描述符(fd)与 文件指针(FILE*)C语言中fopen()函数的使用方法【C 语言】文件操作 ( fread 函数 )【C 语言】文件操作 ( fwrite 函数 )

在发送端我们可以看到这样的代码(虽然可能中间隔得有点远):

FILE *fd;

fd = fopen( filepath , "r+" );

length = fread( pro , sizeof(char) , sizeof(pro) , fd );

在接收端我们可以看到这样的代码(虽然可能中间隔得有点远):

FILE *fd;

fd=fopen( filepath , "w+" );

fwrite( pro , sizeof(char) , length , fd );

结合刚刚补充的知识,双端对照着看就能看出,发送端是以只读方式打开了目标路径filepath下的目标文件(文件路径要求具体到文件),并使用fread函数将目标文件的数据读入了发送端的缓冲区pro,读入数据的大小为缓冲区pro的大小;而接收端是以可写方式先在目标路径filepath下创建了一个同名文件,并使用fwrite函数将接收端缓冲区pro里面的数据写入了刚刚创建的同名文件中,写入的数据大小为发送端fread函数实际读到数据的大小。经过这样的双端对比观察,文件的数据来源也就明确了。

至此,我们已经对大体的UDP数据收发代码实现原理有了基本完整的认识,程序其他的部分基本就是其他的功能了。另说一句题外话,按我的毕设体感来讲,就是不要轻信任何人写的东西,当然你们等下后面看我写的程序也要这样想哦,要不停的自己判断对错

第三步,实现在指定文件夹内所有文件的批量逐个发送

此时我们已经解决了双端之间单个文件的简单收发,按照我们的功能需求,要实现在指定文件夹内所有文件的批量逐个发送。(这个实现方法是我一拍脑子想的,可能有更正规、更巧妙的方法,但我不知道,这就需要同学们去自行扩展了。)

这个功能其实不难,主要就是要彻底理解fopen函数的用法,在此基础上对它操作的绝对路径进行一点点修改,让最后的文件名变成一个变量,目标文件夹路径设置成一个字符常量,然后不断获取目标文件的名字,将文件名与文件夹路径重新拼接成为fopen操作的绝对路径,这样问题就转化成了如何获取所有目标文件的名字,即遍历目标文件夹,获取文件夹中的所有文件名,给个链接文章,Linux C 读取文件夹下所有文件(包括子文件夹)的文件名,这篇文章讲了如何遍历文件夹获取文件名。咱们想要的整体功能实现起来什么样子,上一小段我自己的部分代码看看大概意思算了,注意,为了看着方便,我省略了传输的代码,以下是在发送端获取文件名的代码。

DIR *dir;
//storage the return of opendir---the point of first directory 

if ((dir=opendir(Path)) == NULL)
{
    perror("Open dir error...");
    exit(1);
}

while((pir = readdir(dir))!=NULL)
//遍历整个文件夹就停止循环
{
    if(pir->d_type == 8)
    //find all the file, send them to client one by one
    {
        strncpy(pro,pir->d_name,sizeof(pir->d_name));
        //storage the file name
        sprintf(filepath,"%s%s",Path,pir->d_name);
        //combine the path and filename
    }
}

while循环的功能是循环使用opendir函数,依次获取目标路径Path下的所有文件名,发送端获取到一个文件名后,如果类型为8(8是文件),那么就将这个文件名赋给缓冲区、发给接收端,并将文件夹路径Path和这个获取到的文件名d_name组合成fopen函数要操作的绝对路径filepath,后续再在这个while循环中增加文件数据传输的对应代码,就能实现指定文件夹内所有文件的批量逐个发送。

第四步,实现较大文件的传输等停

我们已经实现了批量发送,那么就只剩下一个事情了,实现较大文件的传输等停。(另外补充,如果功能实现框架较复杂,操作条件判断的收发也需要编写等停代码或重传机制)

这个等停的逻辑其实挺简单,若服务端server忙标志为1,则服务端进行数据发送,发送过程中客户端client忙标志为0,发送结束则设置服务端server忙标志为0、客户端client忙标志为1;若服务端server忙标志为0,则客户端进行数据接收,接收过程中客户端client忙标志为1,接收结束则设置服务端server忙标志为1、客户端client忙标志为0。但注意,我当时项目结题时间已经严重不足了,所以没有设置任何的丢包重传机制,正常肯定是要写丢包重传的,而且补充一句写论文描述这个东西最好用泳道图,流程图不好画。

以下是实现的代码(等到后来又跑起一次但是发现了当时没发生的奇怪bug,不过思想还是可以用的):

代码补充说明,传输数据和忙信号的socket是不一样的,所以传输语句里面的目标地址有点儿差别,把代表中心思想的pro结构体和busy结构体放上来看看:

struct protocol
{
	int command;
    //record the command input by user
	int len;
    //the length of buffer
	int num;
    //the number of file
	char buf[MAXSIZE];
    //the buffer of filedata or filename
};

struct busy
{
	int server;
    //the working flag of server
	
    int client;
    //the working flag of client
};

struct protocol pro;

struct busy busy;

服务端功能实现示意代码:

//initial server busy flag = 1(working), means start to send data
busy.server=1;
busy.client=0;

//send busy flag to client,let client wait for data
sendto(sockby,&busy,sizeof(busy),0,(struct sockaddr *)&client_busy,len_clientbusy);

while(1)
//keep reading data until the object file have been totally read. 
{
	recvfrom(sockby,&busy,sizeof(busy),0,(struct sockaddr *)&client_busy,&len_clientbusy);
    //keep receiving the client busy flag

	if(busy.client == 0)
    //waiting for client receive over
	{
        //read data from the found file until pro.buf fulled, meanwhile return the data length in one read
		length = fread(pro.buf,sizeof(char),sizeof(pro.buf),fd);
		pro.len = length;
        //set data read length
        //send informations to client, meanwhile record sendto condition
		Sent_Result=sendto(sockfd,&pro,sizeof(pro),0,(struct sockaddr *)&client_data,len_client);
        
        //set working flag
		busy.server=0;
		busy.client=1;
        //send working flag to client
		sendto(sockby,&busy,sizeof(busy),0,(struct sockaddr *)&client_busy,len_clientbusy);
								
		if(length == 0)
        //if file totally read, fread will return the data length == 0
		{
			bzero(&pro.buf,sizeof(pro.buf));
            //clean the pro.buf for filename input
			printf("File%i: '%s' DOWNLOAD over!\n\n",num,filepath);
			break;
		}
}

 客户端功能实现示意代码:

while(1)
//keep receiving filedata from pro.buf,until the file have been totally read.
{
	recvfrom(sockby,&busy,sizeof(busy),0,(struct sockaddr *)&server_busy,&len_serverbusy);
    //keep receiving the busy flag

	if(busy.server == 0)
    //waiting for server send filename/data over, if over, busy.server == 0
	{
		Receive_Result = recvfrom(sockfd,&pro,sizeof(pro),0,(struct sockaddr *)&server_data,&len_server);
		length = pro.len;
		fwrite(pro.buf,sizeof(char),length,fd);
									
		busy.server=1;
		busy.client=0;
		sendto(sockby,&busy,sizeof(busy),0,(struct sockaddr *)&server_busy,len_serverbusy);
									
		if(length == 0)
        //one file has been totally read 
		{
			bzero(&pro.buf,sizeof(pro.buf));
            //clean the pro.buf for filename input
			printf("File%i : '%s' DOWNLOAD over!\nThe DOWNLOADed file storage in '%s'.\n",num,filepath_server,filepath_client);
            break;
		}

        if (Receive_Result == -1)
        //if the receive error
		{
            printf("File%i : '%s' recvfrom failed with errNO[%d]errMsg[%s]\n",num,filepath_server,errno,strerror(errno));
		    close(sockfd);
			close(sockby);
		    exit(1);
		}
}

采用这样的方式可以实现简单的等停,但程序实际上板之后,因为板子运行速度和电脑运行速度的差异,还需要另行调试,具体问题具体分析吧。

上篇结语

至此,对于本文工作的硬件与软件部分的重点内容讲解已经全部完成,即硬件PCB设计与Linux环境下C语言使用UDP/IP协议的批量文件存取软件程序编写,本文上篇结束,下篇为实现软件的各种细枝末节,回家再说。

(2023.6.13日1点10分,工大A01,刚打完一局梦境大乱斗,玩小西施杀13个,开心~)

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
《01米联客2020版Zynq MPSOC SDK篇-20210527》是一篇关于2020版Zynq MPSOC SDK的技术文档。该文档主要介绍了使用Zynq MPSOC SDK开发软件的基本流程和步骤。 文档首先简要介绍了Zynq MPSOC SDK的特点和功能,指出该软件开发套件可以为Zynq MPSOC芯片提供全面的软件开发环境,并支持多种编程语言和开发工具。 接着,文档详细介绍了Zynq MPSOC SDK的安装和配置过程。首先需要准备好开发环境和相关工具,然后按照文档提供的步骤进行安装和配置。文档给出了详细的图文说明,使读者可以轻松地完成这些操作。 在安装和配置完成后,文档介绍了使用Zynq MPSOC SDK进行软件开发的基本流程。首先,需要创建一个新的工程,并选择适合的模板。然后,可以根据实际需求,添加所需的库文件和驱动程序。接下来,可以编写和调试代码,并使用MPSOC SDK提供的调试工具进行调试。 文档还介绍了Zynq MPSOC SDK的一些高级功能,如使用硬件抽象层(HAL)进行底层驱动开发,以及使用RTOS进行多线程开发等。这些功能可以帮助开发者更好地利用Zynq MPSOC芯片的强大性能和功能。 总的来说,《01米联客2020版Zynq MPSOC SDK篇-20210527》是一篇系统介绍了2020版Zynq MPSOC SDK的技术文档。通过阅读该文档,读者可以全面了解并掌握使用Zynq MPSOC SDK进行软件开发的方法和技巧,帮助他们更高效地进行嵌入式软件开发工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值