1. 基于UDP的TFTP文件传输上传下载完整版本

1)tftp协议概述

简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输

特点:

是应用层协议

基于UDP协议实现

数据传输模式

octet:二进制模式(常用)

mail:已经不再支持

2)tftp下载模型

TFTP通信过程总结

  1. 服务器在69号端口等待客户端的请求
  2. 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
  3. 每个数据包的编号都有变化(从1开始)
  4. 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
  5. 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。

3)tftp协议分析

差错码:

0 未定义,差错错误信息

1 File not found.

2 Access violation.

3 Disk full or allocation exceeded.

4 illegal TFTP operation.

5 Unknown transfer ID.

6 File already exists.

7 No such user.

8 Unsupported option(s) requested.

#include <head.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PORT 69               //1024~49151
#define IP "192.168.122.49"    //int do_download(int sfd, struct sockaddr_in sin);
int do_upload(int sfd, struct sockaddr_in sin);int main(int argc, const char *argv[])
{//创建报式套接字int sfd = socket(AF_INET, SOCK_DGRAM, 0);if(sfd < 0){ERR_MSG("socket");return -1;}printf("socket create success  sfd=%d __%d__\n", sfd, __LINE__);//绑定--->非必须绑定//如果不绑定,操作系统会自动绑定本机IP,从49152~65535范围内随机一个未被使用的端口号//填充服务器的地址信息结构体//供给下面的sendto使用,struct sockaddr_in sin;sin.sin_family         = AF_INET;     //必须填AF_INET;sin.sin_port         = htons(PORT);     //服务器绑定的端口,网络字节序sin.sin_addr.s_addr = inet_addr(IP);     //服务器绑定的IP,本机IP ifconfigchar choose = 0;while(1){printf("----------------------------\n");printf("----------1. 下载-----------\n");printf("----------2. 上传-----------\n");printf("----------3. 退出-----------\n");printf("----------------------------\n");printf("请输入>>> ");choose = getchar();while(getchar()!=10);switch(choose){case '1':do_download(sfd, sin);break;case '2':do_upload(sfd, sin);break;case '3':goto END;break;default:printf("输入错误,请重新输入\n");break;}}
END://关闭所有文件描述符close(sfd);return 0;
}int do_upload(int sfd, struct sockaddr_in sin)
{int ret_val = 0;char filename[20] = "";printf("请输入要上传的文件名>>> ");scanf("%s", filename);while(getchar()!=10);//判断文件存不存在int fd = open(filename, O_RDONLY);if(fd < 0){ERR_MSG("open");return -1;}//发送上传请求//组上传请求包char buf[516] = "";char *ptr = buf;unsigned short *p1 = (unsigned short*)buf;*p1 = htons(2);char *p2 = buf + 2;strcpy(p2,filename);char *p3 = p2 + strlen(p2)+1;strcpy(p3,"octet");//计算大小int size =2+strlen(filename)+7;//sendto if(sendto(sfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}socklen_t addrlen = sizeof(sin);ssize_t res = 0;unsigned short num = 0;      //本地记录的快编号while(1){bzero(buf, sizeof(buf));//接收服务器的应答res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, &addrlen);if(res < 0){ERR_MSG("recvfrom");ret_val = -1;break;}//printf("res = %ld   __%d__\n", res, __LINE__);//printf("0:%d 1:%d 2:%d 3:%d\n", buf[0], buf[1], buf[2], buf[3]);if(4 == buf[1]){if(htons(num) == *(unsigned short*)(buf+2)){//组数据包给服务器num++;*(unsigned short*)buf = htons(3);*(unsigned short*)(buf+2) = htons(num);res = read(fd, buf+4, 512);if(res < 0){ret_val = -1;break;}else if(0 == res){printf("文件:%s 上传完毕\n", filename);break;}//发送数据包if(sendto(sfd, buf, res+4, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}}}else if(5 == buf[1])//错误包{printf("MSG_ERR: code[%d] msg[%s] __%d__\n", \ntohs(*(unsigned short*)(buf+2)), buf+4, __LINE__);ret_val = -1;break;}}close(fd);return ret_val;
}int do_download(int sfd, struct sockaddr_in sin)
{int ret_val = 0;char buf[516] = "";char filename[20] = "";printf("请输入要下载的文件名>>> ");scanf("%s", filename);while(getchar()!=10);//发送下载请求//组协议包char *ptr = buf;unsigned short *p1 = (unsigned short*)buf;*p1 = htons(1);char *p2 = buf + 2;strcpy(p2,filename);char *p3 = p2 + strlen(p2)+1;strcpy(p3,"octet");//计算大小int size =2+strlen(filename)+7;//sendto if(sendto(sfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}//本地创建并打开要下载的文件int fd = -1;     //必须初始化一个无意义的文件描述符,否则下面的closesocklen_t addrlen = sizeof(sin);ssize_t res = 0;unsigned short num = 0;      //本地记录的快编号//循环接收数据包,回复ACkwhile(1){bzero(buf, sizeof(buf));//接收数据包res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, &addrlen);if(res < 0){ERR_MSG("recvfrom");ret_val = -1;break;}//printf("res = %ld   __%d__\n", res, __LINE__);//printf("0:%d 1:%d 2:%d 3:%d\n", buf[0], buf[1], buf[2], buf[3]);//if(ntohs(*(unsigned short*)buf) == 3)//由于操作码占两个字节的无符号整数,所以传输的时候以大端传输//所有有效差错码,会存储在高地址上,即存储在buf[1]位置,buf[0]中存储的是0if(3 == buf[1])     //数据包{//UDP可能会数据重复,为了防止重复收包。//则在本地记录了一下服务器回过来的快编号,每次处理前,先判断快编号是否正确if(htons(num+1) == *(unsigned short*)(buf+2)){num++;if(-1 == fd){fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664);if(fd < 0){ERR_MSG("open");ret_val = -3;break;}}//将获取到的数据,写入到文件中if(write(fd, buf+4, res-4) < 0){ERR_MSG("write");ret_val = -3;break;}//回复ACK,数据包前四个字节与ACK包基本一致//只有操作码不一致,有效操作码在buf[1]的位置,直接将buf[1]从3,修改成4即可。buf[1] = 4;if(sendto(sfd, buf, 4, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");ret_val = -1;break;}if(res-4 < 512){printf("======= 文件下载完毕 =======\n");break;}}}else if(5 == buf[1])//错误包{printf("MSG_ERR: code[%d] msg[%s] __%d__\n", \ntohs(*(unsigned short*)(buf+2)), buf+4, __LINE__);ret_val = -1;break;}}close(fd);return ret_val;
}

head.h文件

#ifndef __HEAD_H__
#define __HEAD_H__#define ERR_MSG(msg) do{\fprintf(stderr,"__%d__:",__LINE__);\perror(msg);\
}while(0)
#include <dirent.h>           // 目录项  
#include <fcntl.h>            // 文件控制  
#include <fnmatch.h>          // 文件名匹配类型  
#include <glob.h>             // 路径名模式匹配类型  
#include <grp.h>              // 组文件  
#include <netdb.h>            // 网络数据库操作  
#include <pwd.h>              // 口令文件  
#include <regex.h>            // 正则表达式  
#include <tar.h>              // TAR归档值  
#include <termios.h>          // 终端I/O  
#include <unistd.h>           // 符号常量  
#include <utime.h>            // 文件时间  
#include <wordexp.h>          // 字符扩展类型   
#include <arpa/inet.h>        // INTERNET定义  
#include <net/if.h>   	 	  // 套接字本地接口  
#include <netinet/in.h> 	  // INTERNET地址族  
#include <netinet/tcp.h>  	  // 传输控制协议定义   
#include <sys/mman.h> 	      // 内存管理声明  
#include <sys/select.h>   	  // Select函数  
#include <sys/socket.h>    	  // 套接字借口  
#include <sys/stat.h>   	  // 文件状态  
#include <sys/times.h>    	  // 进程时间  
#include <sys/types.h>   	  // 基本系统数据类型  
#include <sys/un.h>    		  // UNIX域套接字定义  
#include <sys/utsname.h>  	  // 系统名  
#include <sys/wait.h>   	  // 进程控制  #include <cpio.h>   		  // cpio归档值  
#include <dlfcn.h>   		  // 动态链接  
#include <fmtmsg.h>   		  // 消息显示结构  
#include <ftw.h>         	  // 文件树漫游  
#include <iconv.h>   		  // 代码集转换使用程序  
#include <langinfo.h>  		  // 语言信息常量  
#include <libgen.h>    		  // 模式匹配函数定义  
#include <monetary.h>    	  // 货币类型  
//#include <ndbm.h>    		  // 数据库操作  
#include <nl_types.h> 		  // 消息类别  
#include <poll.h>   		  // 轮询函数  
#include <search.h>  		  // 搜索表  
#include <strings.h>   		  // 字符串操作  
#include <syslog.h>   		  // 系统出错日志记录  
#include <ucontext.h>   	  // 用户上下文  
#include <ulimit.h>   		  // 用户限制  
#include <utmpx.h>   		  // 用户帐户数据库  
#include <sys/ipc.h>          // IPC(命名管道)  
#include <sys/msg.h>   		  // 消息队列  
#include <sys/resource.h> 	  // 资源操作  
#include <sys/sem.h>    	  // 信号量  
#include <sys/shm.h>   		  // 共享存储  
#include <sys/statvfs.h>  	  // 文件系统信息  
#include <sys/time.h>      	  // 时间类型  
#include <sys/timeb.h>  	  // 附加的日期和时间定义  
#include <sys/uio.h>    	  // 矢量I/O操作 #include <aio.h>        	  // 异步I/O  
#include <mqueue.h>   		  // 消息队列  
#include <pthread.h>   		  // 线程  
#include <sched.h>    		  // 执行调度  
#include <semaphore.h>  	  // 信号量  
#include <spawn.h>     		  // 实时spawn接口  
#include <stropts.h>    	  // XSI STREAMS接口  
//#include <trace.h>    		  // 事件跟踪#include <assert.h>    //设定插入点  
#include <ctype.h>     //字符处理  
#include <errno.h>     //定义错误码  
#include <float.h>     //浮点数处理  
#include <iso646.h>        //对应各种运算符的宏  
#include <limits.h>    //定义各种数据类型最值的常量  
#include <locale.h>    //定义本地化C函数  
#include <math.h>     //定义数学函数  
#include <setjmp.h>        //异常处理支持  
#include <signal.h>        //信号机制支持  
#include <stdarg.h>        //不定参数列表支持  
#include <stddef.h>        //常用常量  
#include <stdio.h>     //定义输入/输出函数  
#include <stdlib.h>    //定义杂项函数及内存分配函数  
#include <string.h>    //字符串处理  
#include <time.h>     //定义关于时间的函数  
#include <wchar.h>     //宽字符处理及输入/输出  
#include <wctype.h>    //宽字符分类
#endif
#include <head.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#define PORT 6666               //1024~49151
#define IP  "192.168.122.15"     //IP地址,本机IP ifconfigstruct cli_msg
{int newfd;struct sockaddr_in cin;
};void* deal_cli_msg(void* arg);int main(int argc, const char *argv[])
{//创建流式套接字int sfd = socket(AF_INET, SOCK_STREAM, 0); if(sfd < 0){   ERR_MSG("socket");return -1; }   printf("socket create success  sfd = %d\n", sfd);//设置允许端口快速被重用int resue = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue)) < 0){   ERR_MSG("setsockopt");return -1; }   //填充服务器的地址信息结构体//真实的地址信息结构体根据地址族执行,AF_INET: man 7 ipstruct sockaddr_in sin;sin.sin_family      = AF_INET;      //必须填AF_INET;sin.sin_port        = htons(PORT);  //端口号的网络字节序,1024~49151sin.sin_addr.s_addr = inet_addr(IP);    //IP地址的网络字节序,ifconfig查看//绑定---必须绑定if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0){   ERR_MSG("bind");return -1; }   printf("bind success ");//将套接字设置为被动监听状态if(listen(sfd, 128) < 0){   ERR_MSG("listen");return -1; }   printf("listen success ");//功能:阻塞函数,阻塞等待客户端连接成功。//当客户端连接成功后,会从已完成连接的队列头中获取一个客户端信息,//并生成一个新的文件描述符;新的文件描述符才是与客户端通信的文件描述符struct sockaddr_in cin;     //存储连接成功的客户端的地址信息socklen_t addrlen = sizeof(cin);int newfd = -1; pthread_t tid;struct cli_msg info;while(1){   //主线程负责连接//accept函数阻塞之前,会先预选一个没有被使用过的文件描述符//当解除阻塞后,会判断预选的文件描述符是否被使用//如果被使用了,则重新遍历一个没有被用过的//如果没有被使用,则直接返回预先的文件描述符;newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){ERR_MSG("accept");return -1; }printf("[%s : %d] newfd=%d 客户端连接成功\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);info.newfd = newfd;info.cin = cin;//能运行到当前位置,则代表有客户端连接成功//则需要创建一个分支线程用来,与客户端交互if(pthread_create(&tid, NULL, deal_cli_msg, &info) != 0){fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);return -1; }pthread_detach(tid);        //分离线程}   //关闭所有套接字文件描述符close(sfd);return 0;
}//线程执行体
void* deal_cli_msg(void* arg)   //void* arg = (void*)&info
{//---------问题处------int newfd = ((struct cli_msg*)arg)->newfd;struct sockaddr_in cin = ((struct cli_msg*)arg)->cin;char buf[128] = ""; ssize_t res = -1; while(1){   bzero(buf, sizeof(buf));//接收res = recv(newfd, buf, sizeof(buf), 0); if(res < 0){ERR_MSG("recv");break;}else if(0 == res){fprintf(stderr, "[%s : %d] newfd=%d 客户端下线\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);break;}printf("[%s : %d] newfd=%d : %s\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, buf);//发送 -- 将数据拼接一个 *_* 发送回去strcat(buf, "*_*");if(send(newfd, buf, sizeof(buf), 0) < 0){ERR_MSG("send");break;}printf("send success\n");}   close(newfd);pthread_exit(NULL);
}

上述程序中

1. 多线程中的newfd,能否修改成全局,不行,为什么?
2. 多线程中分支线程的newfd能否不另存,直接用指针间接访问主线程中的newfd,不行,为什么?

    //必须要另存,因为同一个进程下的线程共享其附属进程的所有资源
    //如果使用全局,则会导致每次连接客户端后, newfd和cin会被覆盖
    //如果使用指针间接访问外部成员变量,也会导致,成员变量被覆盖。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/90189.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Install And Understand APISIX(Master the knowledge of APISIX)

Master the knowledge of APISIX Install And Understand APISIX 环境准备 接口服务&#xff1a;gpt 接口服务&#xff08;使用 spring boot 编写的 Chat GPT 接口服务&#xff09; 调用接口示例&#xff1a; 虚拟机软件&#xff1a;VMware Workstation Pro 17 Linux 镜像&…

大数据Flink(六十三):SqlClient工具的使用

文章目录 SqlClient工具的使用 一、​​​​​​​入门

(二分查找) 11. 旋转数组的最小数字 ——【Leetcode每日一题】

❓剑指 Offer 11. 旋转数组的最小数字 难度&#xff1a;简单 把一个数组最开始的若干个元素搬到数组的末尾&#xff0c;我们称之为数组的旋转。 给你一个可能存在 重复 元素值的数组 numbers &#xff0c;它原来是一个升序排列的数组&#xff0c;并按上述情形进行了一次旋转…

分布式唯一ID实战

目录 一、UUID二、数据库方式1、数据库生成之简单方式2、数据库生成 - 多台机器和设置步长&#xff0c;解决性能问题3、Leaf-segment 方案实现4、双 buffer 优化5、Leaf高可用容灾 三、基于Redis实现分布式ID四、雪花算法 一、UUID UUID的标准形式包含32个16进制数字&#xff…

UE5 实现残影效果

文章目录 前言实现效果示例1示例2示例3示例4实现扩展前言 本文采用虚幻5.2.1版本,对角色生成残影效果进行讲解,以一种简单的、通俗易懂的、高性能的方式来实现此效果。此效果可以在角色使用某一技能时触发,比如使用攻击招式、闪现等等。 实现效果 示例1 在昏暗的环境示例…

用 oneAPI 实现 AI 欺诈检测:一款智能图像识别工具

简介 虚假图像和视频日益成为社交媒体、新闻报道以及在线内容中的一大隐患。在这个信息爆炸的时代&#xff0c;如何准确地识别和应对这些虚假内容已经成为一个迫切的问题。为了帮助用户更好地辨别虚假内容&#xff0c;我开发了一款基于 oneAPI、TensorFlow 和 Neural Compress…

百日筑基篇——python爬虫学习(一)

百日筑基篇——python爬虫学习&#xff08;一&#xff09; 文章目录 前言一、python爬虫介绍二、URL管理器三、所需基础模块的介绍1. requests2. BeautifulSoup1. HTML介绍2. 网页解析器 四、实操1. 代码展示2. 代码解释1. 将大文件划分为小的文件&#xff08;根据AA的ID数量划…

【无监督】2、MAE | 自监督模型提取的图像特征也很能打!(CVPR2022 Oral)

文章目录 一、背景二、方法三、效果 论文&#xff1a;Masked Autoencoders Are Scalable Vision Learners 代码&#xff1a;https://github.com/facebookresearch/mae 出处&#xff1a;CVPR2022 Oral | 何凯明 | FAIR 一、背景 本文的标题突出了两个词&#xff1a; masked…

【佳佳怪文献分享】安全人机交互的学习责任分配与自动驾驶应用

标题&#xff1a;Learning Responsibility Allocations for Safe Human-Robot Interaction with Applications to Autonomous Driving 作者&#xff1a;Ryan K. Cosner, Yuxiao Chen, Karen Leung, and Marco Pavone 来源&#xff1a;2023 IEEE International Conference on …

设备管理系统能起到什么作用?

在现代工业运营中&#xff0c;设备的高效管理和维护对于保障生产稳定运行和提升企业竞争力至关重要。而设备管理系统作为一种关键工具&#xff0c;能够极大地提高企业的生产效率和设备维护的准确性。本文将深入探讨设备管理系统的作用&#xff0c;以PreMaint设备数字化平台为例…

sealos安装k8s

一、前言 1、我前面文章有写过使用 kubeadm 安装的方式&#xff0c;大家可以去参考 &#xff08;二&#xff09;k8s集群安装&#xff0c;有一系列的k8s文章说明 2、安装k8s的方式有很多 kubeadmsealoskubespray等等 3、关于sealos来安装 k8s &#xff0c;也是非常建议大家去…

基于自适应曲线阈值和非局部稀疏正则化的压缩感知图像复原研究【自适应曲线阈值去除加性稳态白/有色高斯噪声】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

P450进阶款无人机室内定位功能研测

在以往的Prometheus 450&#xff08;P450&#xff09;无人机上&#xff0c;我们搭载的是Intel Realsense T265定位模块&#xff0c;使用USB连接方式挂载到机载计算机allspark上&#xff0c;通过机载上SDK驱动T265运行并输出SLAM信息&#xff0c;以此来实现室内定位功能。 为进…

SpringBoot复习:(45)@Component定义的bean会被@Bean定义的同名的bean覆盖

有同名的bean需要配置&#xff1a; spring.main.allow-bean-definition-overridingtrue 否则报错。 package cn.edu.tju.component;import org.springframework.stereotype.Component;Component public class Person {private String name;private int age;{this.name "…

C++ QT(一)

目录 初识QtQt 是什么Qt 能做什么Qt/C与QML 如何选择Qt 版本Windows 下安装QtLinux 下安装Qt安装Qt配置Qt Creator 输入中文配置Ubuntu 中文环境配置中文输入法 Qt Creator 简单使用Qt Creator 界面组成Qt Creator 设置 第一个Qt 程序新建一个项目项目文件介绍项目文件*.pro样式…

C++多态

文章目录 &#x1f435;1. 什么是多态&#x1f436;2. 构成多态的条件&#x1f429;2.1 虚函数&#x1f429;2.2 虚函数的重写&#x1f429;2.3 final 和 override关键字&#x1f429;2.4 重载、重写、重定义对比 &#x1f431;3. 虚函数表&#x1f42f;4. 多态的原理&#x1f…

深入探究QCheckBox的三种状态及其用法

文章目录 引言&#xff1a;三种状态一、未选中状态&#xff08;0&#xff09;&#xff1a;二、选中状态&#xff08;2&#xff09;&#xff1a;三、部分选中状态&#xff08;1&#xff09;&#xff1a; 判断方法结论&#xff1a; 引言&#xff1a; QCheckBox是Qt框架中常用的复…

分支语句与循环语句(2)

3.3 do...while()循环 3.3.1 do语句的语法&#xff1a; do 循环语句; while(表达式); 3.3.2执行流程图&#xff1a; 3.3.3 do语句的特点 循环至少执行一次&#xff0c;使用的场景有限&#xff0c;所以不是经常使用。 打印1-10的整数&#xff1a; #define _CRT_SECURE_NO_WA…

网页显示摄像头数据的方法---基于web video server

1. 背景&#xff1a; 在ros系统中有发布摄像头的相关驱动rgb数据&#xff0c;需求端需要将rgb数据可以直接在网页上去显示。 问题解决&#xff1a; web_video_server功能包&#xff0c;相关链接&#xff1a; web_video_server - ROS Wiki 2. 下载&#xff0c;安装和编译&a…