作业:基于udp的tftp文件传输实例

#include <head.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
 
#define PORT 69              //服务器绑定的端口号
#define IP "192.168.1.107" //服务器的IP地址
 
int do_download(int cfd,struct sockaddr_in sin);
int do_upload(int cfd, struct sockaddr_in sin);
 
int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(cfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("cfd = %d\n",cfd);
 
	//绑定客户端的地址信息结构体到套接字上--->非必须绑定
	//若不绑定,则操作系统会给客户端绑定运行主机的IP和随机的端口号
 
	//填充要连接的服务器地址信息结构体,真实的地址信息结构体根据地址族制定
	//要发给谁,就填谁的地址信息
	//AF_INET : man 7 ip
	struct sockaddr_in sin;
	socklen_t addrlen=sizeof(sin);
	sin.sin_family         = AF_INET;       //必须填AF_INET
	sin.sin_port           = htons(PORT);   //端口号:服务器绑定的端口号
	sin.sin_addr.s_addr    = inet_addr(IP); //服务器绑定的IP
 
	char choose =0;
	while(1)
	{
		printf("*******************\n");
		printf("******1. 下载******\n");
		printf("******2. 上传******\n");
		printf("******3. 退出******\n");
		printf("*******************\n");
		printf("*******************\n");
		printf("请输入>>> ");
 
		choose = getchar();
		while(getchar() != 10);   //循环获取字符,直到遇到\n结束循环
 
		switch(choose)
		{
		case '1':
			do_download(cfd,sin);
			break;
		case '2':
			do_upload(cfd,sin);
			break;
		case '3':
			goto END;
			break;
		default:
			printf("输入错误!请重新输入\n");
 
		}
	}
END:
	//关闭文件描述符
	close(cfd);
 
	return 0;
}
 
int do_download(int cfd, struct sockaddr_in sin)
{
	//组包准备发送下载请求
	char buf[516]="";
	char name[20]="";
	printf("请输入要下载的文件名>>> ");
	scanf("%s",name);
	while(getchar()!=10);
 
	unsigned short *p1=(short*)buf;   //操作码
	*p1=htons(1);
 
	char *p2=buf+2;          //文件名
	strcpy(p2,name);
 
	char *p3=p2+strlen(p2); //第一个0 
	*p3=0;
 
	char *p4=p3+1;   //模式  
	strcpy(p4,"octet");
 
	size_t size=2+strlen(p2)+1+strlen(p4)+1; //操作码+文件名+0+模式+0
 
	//发送下载请求
	if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}
 
	//创建下载文件并清空
	int fd = -1;   //必须初始化成一个无效的文件描述符
	socklen_t addrlen = sizeof(sin);
	ssize_t res = 0;
	unsigned short num = 0;  //记录本地的块编号
 
	//发送下载请求
	while(1)
	{
		//接收数据
		bzero(buf,sizeof(buf));
		res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
		if(res < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
 
		if(3 == buf[1])  //数据包
		{
			//判断服务器返回的数据包的块编号与本地记录的块编号是否一致
			if(*(unsigned short*)(buf+2) == htons((num+1)))
			{
				num++;  //更新本地记录的块编号
				if(-1 == fd)
				{
					fd=open(name,O_WRONLY|O_CREAT|O_TRUNC,0664);
					if(fd < 0)
					{
						perror("open");
						return -1;
					}
				}
 
				//将数据写到文件中
				if(write(fd,buf+4,res-4) < 0 )
				{
					perror("write");
					return -1;
				}
 
				//发送ACK
				buf[1] = 4;
				//*p1=htons(4);
				if(sendto(cfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
				{
					ERR_MSG("sendto");
					return -1;
				}
				//若接收到的数据小于512跳出循环,结束下载
				if(res-4 < 512)
				{
					printf("%s 文件下载完毕\n",name);
					break;
				}
			}
		}
		else if(5 == buf[1])    //错误包
		{
			printf("错误: %d %s\n",ntohs(*(short*)(buf+2)),buf+4);
			close(fd);
			return -1;
		}
	}
	close(fd);
	return 0;
}
 
 
int do_upload(int cfd, struct sockaddr_in sin)
{
	//组包准备发送上传请求
	char buf[516]="";
	char name[20]="";
	printf("请输入要上传的文件名>>> ");
	scanf("%s",name);
	while(getchar()!=10);
 
	int fd = open(name,O_RDONLY);
	if(fd < 0)
	{
		if( errno == ENOENT)
		{
			printf(">>>文件不存在,请重新输入<<<\n");
			return -2;
		}
		else
		{
			ERR_MSG("open");
			return -1;
		}
	}
	//组包准备发送上传请求
	unsigned short *p1=(short*)buf;   //操作码
	*p1=htons(2);
 
	char *p2=buf+2;          //文件名
	strcpy(p2,name);
 
	char *p3=p2+strlen(p2); //第一个0 
	*p3=0;
 
	char *p4=p3+1;   //模式  
	strcpy(p4,"octet");
 
	size_t size=2+strlen(p2)+1+strlen(p4)+1; //操作码+文件名+0+模式+0
 
	//发送上传请求
	if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}
 
	//循环接收发送数据包
	ssize_t res;
	unsigned short num = 0;
	socklen_t addrlen = sizeof(sin);
	while(1)
	{
		//将数据从文件中读取到buf中
		bzero(buf,sizeof(buf));
		res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
		if(res < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
 
		//操作码的范围是1-5,因为是网络字节序
		//所以有效操作码存储在高位,即buf[1]的位置
		//printf("buf[1] = %d\n",buf[1]);   //4 服务器返回应答包
		if(4 == buf[1])  //数据包
		{
			//判断当前数据包的编号是否等于应答包的编号
			//防止数据包在传送过程丢包或重复收包
			if(num == ntohs(*(unsigned short*)(buf+2)))
			{
				//修改操作码为数据包
				buf[1] = 3;
				//填充块编号
				num++;
				*(unsigned short*)(buf+2) = htons(num);
 
				//读取数据
				res = read(fd,buf+4,sizeof(buf)-4);
				if(res < 0)
				{
					ERR_MSG("read");
					return -1;
				}
				else if(0 == res)
				{
					printf("%s 文件上传完毕!\n",name);
					break;
				}
 
				//发送数据包
				//发送的数据包大小为,读取到的字节数res+操作码2byte+块编号2bytes
				if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
				{
					ERR_MSG("sendto");
					return -1;
				}
			}
			else
			{
				printf("文件上传失败,请检查网络环境\n");
					break;
			}
		}
		else if(5 == buf[1])    //错误包
		{
			printf("错误: %d %s\n",ntohs(*(short*)(buf+2)),buf+4);
			close(fd);
			return -1;
		}
	}
	close(fd);
	return 0;
}

导图:

面试题:

IP地址:网络中主机的标识符

TCP服务端通信流程:创建套接字,然后绑定服务器地址,然后开启被动监听,然后就是与客户端的数据收发,最后关闭套接字。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

adobe animate 时间轴找不到编辑多个帧按钮

如题&#xff0c;找了半天&#xff0c;在时间轴上找不到编辑多个帧按钮,导致无法批量处理帧 然后搜索发现原来是有些版本被隐藏了&#xff0c;需要再设置一下 勾选上就好了

怎么进行流程图制作?这种方法一看就会

怎么进行流程图制作&#xff1f;在当今这个信息爆炸的时代&#xff0c;流程图作为一种直观、高效的表达方式&#xff0c;被广泛应用于各种工作场景。无论是项目管理、流程优化&#xff0c;还是产品设计、教育培训&#xff0c;流程图都能帮助我们更好地理解、分析和优化工作流程…

如何查看chrome里network的payload

如何查看chrome的network的请求payload&#xff0c;点击漏斗形状的过滤器&#xff0c;过滤框清空&#xff0c;表示检测所有&#xff0c;右边按钮点击“全部”&#xff0c;“第三方请求”不要勾选。

数字化金融展厅设计要点,你get到了吗?

近年间随着各类数字化主题展厅的出圈&#xff0c;让这种数字多媒体的设计概念逐渐深入至各个领域&#xff0c;这其中也包含了金融主题展厅&#xff0c;与传统展厅不同的是&#xff0c;借助了先进的技术和设备的数字化展厅&#xff0c;能提供更为丰富、个性化的参观体验&#xf…

Java实现定时发送邮件(基于Springboot工程)

1、功能概述&#xff1f; 1、在企业中有很多需要定时提醒的任务&#xff1a;如每天下午四点钟给第二天的值班人员发送值班消息&#xff1f;如提前一天给参与第二天会议的人员发送参会消息等。 2、这种定时提醒有很多方式如短信提醒、站内提醒等邮件提醒是其中较为方便且廉价的…

opengl日记8-opengl创建三角形

文章目录 环境直接上代码一点小总结参考 环境 系统&#xff1a;ubuntu20.04opengl版本&#xff1a;4.6glfw版本&#xff1a;3.3glad版本&#xff1a;4.6cmake版本&#xff1a;3.16.3gcc版本&#xff1a;10.3.0 直接上代码 CMakeLists.txt cmake_minimum_required(VERSION 2…

开源离线语音识别输入工具CapsWriter v1.0——支持无限时长语音、音视频文件转录字幕。

分享一款开源离线语音识别输入工具&#xff0c;支持无限时长语音、音视频文件转录字幕。 软件简介&#xff1a; CapsWriter是一款免费开源且可完全离线识别的语音输入工具&#xff0c;无需担心因在线版本识别带来的各种隐私泄露问题。支持win7及以上的系统&#xff0c;已经更…

TH-FBCQX2防爆气象站

TH-FBCQX2防爆气象站主要适用于易燃易爆、危险性高的场所。以下是其主要的适用领域&#xff1a; 石油与天然气行业&#xff1a;在石油和天然气的生产、储存和运输过程中&#xff0c;防爆气象站可以监测环境中的可燃气体浓度&#xff0c;并根据气象条件预测爆炸风险。同时&…

Kali Linux结合cpolar内网穿透实现公网环境SSH远程访问

文章目录 1. 启动kali ssh 服务2. kali 安装cpolar 内网穿透3. 配置kali ssh公网地址4. 远程连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 简单几步通过[cpolar 内网穿透](cpolar官网-安全的内网穿透工具 | 无需公网ip | 远程访问 | 搭建网站)软件实现ssh 远程连接kali! …

蓝桥杯可撤销并查集|查找|合并|撤销(C++)

前置知识 蓝桥杯并查集|路径压缩|合并优化|按秩合并|合根植物(C)-CSDN博客 可撤销并查集 关键注意 可撤销并查集的撤销功能如何实现可撤销并查集能不能用路径压缩 可撤销并查集(Reversible Union-Find)是一种扩展了标准并查集(Union-Find)数据结构的数据结构&#xff0c;它允…

小北技术圈第一期:技术人员不要只想着用技术变现

每周分享干货内容。寻找100个技术人员&#xff0c;聚在一起副业变现&#xff0c;全程免费&#xff0c;有意者关注微信公众号&#xff1a;小北技术圈。 本期赚钱经 摆脱思维陷阱。 技术人员的副业&#xff0c;不能只局限于开发产品。技术人员有很多独特的优势&#xff0c;这些…

讨论一下测试行业的现状

前言 我这两年尽量都克制自己发这类对行业的看法的文章。在写 23 年总结的时候&#xff0c;我也是只写过往经历&#xff0c;让大家自己判断我的经历是不是有参考价值的&#xff0c;尽量少去劝说其他人按我的思路来。 我对自己说多发技术文章&#xff0c;少发容易引起争议的帖子…

Java进阶 Maven基础

资料格式 配置文件 com.itheima Java代码 Statement stat con.createStatement(); 示例 com.itheima 命令 mvn test - Maven简介 传统项目管理状态分析 Maven 是什么 Maven的本质是一个项目管理工具&#xff0c;将项目开发过程抽象成一个项目对象模型&#xff08;POM&…

torchvision pytorch预训练模型目标检测使用

参考&#xff1a; https://pytorch.org/vision/0.13/models.html https://blog.csdn.net/weixin_42357472/article/details/131747022 有分类、检测、分割相关预训练模型 1、目标检测 https://pytorch.org/vision/0.13/models.html#object-detection-instance-segmentation-…

PytorchAPI的使用及在GPU的使用和优化

API 调用API&#xff1a;和手动实现的思路是一样的。#1&#xff0c;#2这两个步骤是通用的步骤&#xff0c;相当于建立一个模型&#xff0c;之后你具体的数据直接丢进去就行了。只需要按着这样的样式打代码就行&#xff0c;死的东西&#xff0c;不需要你自己创造。 import torc…

MySQL基础之锁

基本概念 锁是一种协调多个事务对同一数据并发访问的一种机制。它确保了数据库的一致性和完整性&#xff0c;防止多个事务同时修改一份数据导致冲突。 锁的类型 锁分为全局锁、表级锁、行级锁。全局锁会锁定整个数据库实例&#xff0c;使其处于只读状态&#xff1b;表级锁会在…

第7篇:全加器的Signal Tap调试

Q&#xff1a;上一篇里我们对全加器进行了功能仿真&#xff0c;这次我们用Quartus软件里的Signal Tap Logic Analyzer工具对全加器进行调试。 A&#xff1a;Signal Tap Logic Analyzer是Quartus Prime设计软件中自带的系统级调试工具&#xff0c;它可以在FPGA设计中采集和显示…

编译原理-实现识别标识符的词法分析器——沐雨先生

实验任务&#xff1a; 实现识别标识符的词法分析器 实验要求&#xff1a; 根据编译原理理论课教材中图2.3“标识符的转换图”&#xff0c;用C语言编写识别标识符的词法分析器&#xff0c;以文本文件为输入&#xff0c;控制台&#xff08;或文件&#xff09;输出识别出的每个…

目标检测——PP-YOLOE-R算法解读

PP-YOLO系列&#xff0c;均是基于百度自研PaddlePaddle深度学习框架发布的算法&#xff0c;2020年基于YOLOv3改进发布PP-YOLO&#xff0c;2021年发布PP-YOLOv2和移动端检测算法PP-PicoDet&#xff0c;2022年发布PP-YOLOE和PP-YOLOE-R。由于均是一个系列&#xff0c;所以放一起解…

java 封装

1、封装概述 &#xff08;1&#xff09;是面向对象三大特征之一(封装&#xff0c;继承&#xff0c;多态)&#xff1b; &#xff08;2&#xff09;是面向对象编程语言对客观世界的模拟&#xff0c;客观世界里的成员变量都隐藏在对象内部&#xff0c;外界无法直接操作。通俗来说…