c实现一个文件下载上传的功能

本文介绍了TFTP协议,它是一个基于UDP的应用层协议,用于网络上的文件传输。TFTP通信过程中,服务器在69号端口监听,客户端请求并接收数据包,每次512Byte,直到传输结束。文章还提供了一个简单的C语言客户端代码示例,展示了下载和上传文件的过程。

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.

服务器下载链接

提取码:6666

客户端代码:

#include <stdio.h> 
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define ERR_MSG(msg) {\
	fprintf(stderr,"line: %d\n",__LINE__);\
	perror(msg);\
}
#pragma pack(1)

#define PORT 69

void do_download(int cfd, struct sockaddr_in sin){
	socklen_t sinlen = sizeof(sin);
	char sendv[128] = "";
	unsigned short *type = (unsigned short *)sendv;
	*type = htons(1);
	char *p = sendv+2;
	strcpy(p,"5.png");
	char *q = sendv+2+strlen(p)+1;
	strcpy(q,"octet");
	ssize_t size = 2+strlen(p)+1+strlen(q)+1;
	sendto(cfd,sendv,size,0,(struct sockaddr *)&sin,sizeof(sin));
	//循环读取,回ACK,当数据钓鱼512时退出循环
	ssize_t res = 0;
	char recvdata[530] = "";
	ssize_t recvsize = 516;
	int fd = open("./5.png",O_WRONLY|O_CREAT|O_TRUNC,0664);
	if(fd < 0){
		ERR_MSG("open");
		return ;
	}
	while(1){
		res = recvfrom(cfd,recvdata,recvsize,0,(struct sockaddr *)&sin, &sinlen);
		if(res < 0){
			ERR_MSG("recvfrom");
			return ;
		}
		else if(res < 516){
			write(fd,recvdata+4,res-4);
			char ACK[8] = "";
			unsigned short *type2 = (unsigned short *)ACK;
			*type2 = htons(4);
			unsigned short *mid = (unsigned short *)(recvdata+2);
			unsigned short *num = (unsigned short *)(ACK+2);
			*num = *mid;
			sendto(cfd,ACK,4,0,(struct sockaddr *)&sin,sizeof(sin));
			break;
		}
		write(fd,recvdata+4,res-4);
		char ACK[8] = "";
		unsigned short *type2 = (unsigned short *)ACK;
		*type2 = htons(4);
		unsigned short *mid = (unsigned short *)(recvdata+2);
		unsigned short *num = (unsigned short *)(ACK+2);
		*num = *mid;
		sendto(cfd,ACK,4,0,(struct sockaddr *)&sin,sizeof(sin));
	}
	close(fd);
	printf("下载完毕\n");
}

void do_upload(int cfd, struct sockaddr_in sin){
	//上传
	socklen_t sinlen = sizeof(sin);
	char upload[128] = "";
	unsigned short *type2 = (unsigned short *)upload;
	*type2 = htons(2);
	char *p2 = upload+2;
	strcpy(p2,"girl.png");
	char *q2 = upload+2+strlen(p2)+1;
	strcpy(q2,"octet");
	ssize_t size2 = 2+strlen(p2)+1+strlen(q2)+1;
	if(sendto(cfd,upload,size2,0,(struct sockaddr *)&sin,sizeof(sin)) < 0){
		ERR_MSG("sendto");
		return ;
	}
	
	int fd2 = open("./girl.png",O_RDONLY);
	if(fd2 < 0){
		ERR_MSG("open");
		return ;
	}

	char uploaddata[530] = "";
	unsigned short *uploadtype = (unsigned short *)uploaddata;
	*uploadtype = htons(3);
	unsigned short n = 0;
	ssize_t uploadsize = 0;
	while(1){
		char uploadrecv[4] = "";
		recvfrom(cfd,uploadrecv,4,0,(struct sockaddr *)&sin, &sinlen);
		if(*(unsigned short *)(uploadrecv+2) == htons(n)){
			n ++;

		}
		//printf("%d %d %d %d\n",uploadrecv[0],uploadrecv[1],uploadrecv[2],uploadrecv[3]);
		
		unsigned short *uploadnums = (unsigned short *)(uploaddata+2);
		*uploadnums = htons(n);
		uploadsize = read(fd2,uploaddata+4,512);
		if(uploadsize < 0){
			ERR_MSG("read");
			return ;
		}
		else if(uploadsize < 512){
			sendto(cfd,uploaddata,uploadsize+4,0,(struct sockaddr *)&sin,sizeof(sin));
			break;
		}
		sendto(cfd,uploaddata,uploadsize+4,0,(struct sockaddr *)&sin,sizeof(sin));
		
	}

}

int main(int argc, const char *argv[])
{
	if(!argv[1]){
		printf("请输入ip地址\n");
		return -1;
	}
	int cfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(cfd < 0){
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success!\n");

	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(argv[1]);
	
	char 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(cfd, sin);
				break;
			case '2':
				do_upload(cfd, sin);
				break;
			case '3':
				goto END;
			dafault:
				printf("输入错误,请重新输入\n");
				break;
		}
	}
END:
	close(cfd);
	return 0;

}

注:我把文件定死了,可以根据需求来修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值