网络编程----基于UDP的TFTP协议,客户端实现上传下载

tftp下载模型
在这里插入图片描述
TFTP通信过程总结

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

tftp协议分析
在这里插入图片描述

上传流程
客户端会向TFTP服务器发送请求写入(WRQ)数据包,指明要写入的文件。如果TFTP服务器允许该文件的写入,就返回一个ACK确认包,该包的编号为0。客户端收到服务器的确认包以后,就开始向服务器写入文件。文件数据以定长512字节进行传输,与下载的传输方式一样,传输的每一个文件数据包都会得到服务器返回的确认包,并且数据包的数据编号也是从1开始。

客户端代码示例

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

#define ERR_MSG(msg)  do{\
	fprintf(stderr, " __%d__ ", __LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.31.13"

int do_download(int sfd,struct sockaddr_in sin)
{
	//发送请求
	char str[777] = "";
	char *ptr = str;
	short int *ptr1 = (short int*)ptr;
	*ptr1 = htons(1);
	char *ptr2 = ptr+2;

	char name[128] = "";
	printf("请输入要下载的文件名>>>");
	scanf("%s",name);
	getchar();
	strcpy(ptr2,name);

	char *ptr3 = ptr2+strlen(ptr2)+1;
	strcpy(ptr3,"octet");

	int size = 2+strlen(ptr2)+1+strlen(ptr3)+1;
	if(sendto(sfd, str, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	printf("sendto success\n");

	struct sockaddr_in rcv_addrmsg; 	//存储接收到的数据包来自哪里
	socklen_t addrlen = sizeof(rcv_addrmsg);

	//打开文件
	int fd = open(name,O_WRONLY|O_TRUNC|O_CREAT,0777);
	if(fd<0)
	{
		ERR_MSG("open");
		return -1;
	}

	short int num = 1;      //块编号
	char str2[1024] = "";   //数据包
	char str3[6] = "";       //ACK
	ssize_t res = 0;
	while(1)
	{
		//接收数据包
		res = recvfrom(sfd,str2,sizeof(str2),0,(struct sockaddr*)&rcv_addrmsg,&addrlen);
		if(res < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}

		//判断操作码是否为5,为5是错误报
		char *sjb = str2;
		short int *err;
		err = (short int*)sjb;
		if(ntohs(*err) == 5)
		{
			printf("error:%s\n",(char*)(err+2));
			return -1;
		}

		//操作码不为5,即为3,即是数据包
		write(fd,sjb+4,res-4);
		short int *kbh = (short int*)str2;
		if(ntohs(*(kbh+1)) != num)
		{
			continue;
		}
		num++;

		char *ack = str3;
		short int *pa1 = (short int*)ack;
		*pa1 = htons(4);
		*(pa1+1) = *(kbh+1);
		
		//发送ACK
		if(sendto(sfd, str3,4,0,(struct sockaddr*)&rcv_addrmsg,addrlen) < 0)
		{
			ERR_MSG("sendto");
			return -1;
		}

		if(res < 516)
		{
			break;
		}
	}
	printf("下载完成\n");

	return 0;

}

int do_upload(int sfd,struct sockaddr_in sin)
{
	//发送写入请求
	char str[777] = "";
	char *ptr = str;
	short int *ptr1 = (short int*)ptr;
	*ptr1 = htons(2);
	char *ptr2 = ptr+2;

	char name[128] = "";
	printf("请输入要上传的文件名>>>");
	scanf("%s",name);
	getchar();
	strcpy(ptr2,name);

	char *ptr3 = ptr2+strlen(ptr2)+1;
	strcpy(ptr3,"octet");

	int size = 2+strlen(ptr2)+1+strlen(ptr3)+1;
	if(sendto(sfd, str, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	printf("sendto success\n");

	char str1[6] = "";                  //接收ACK
	struct sockaddr_in rcv_addrmsg; 	//存储接收到的ACK来自哪里
	socklen_t addrlen = sizeof(rcv_addrmsg);


	//接收ACK确认
	if(recvfrom(sfd,str1,sizeof(str1),0,(struct sockaddr*)&rcv_addrmsg,&addrlen) < 0)
	{
		ERR_MSG("recvfrom");
		return -1;
	}

	//打开文件
	int fd = open(name,O_RDWR);
	if(fd<0)
	{
		ERR_MSG("open");
		return -1;
	}
	
	//封装数据包
	short int num = 1;                       //数据包块编号
	char str2[1024] = "";

	
	//读取文件数据并存入数据包中
	ssize_t rts = 0;
	while(1)
	{
		//封装数据包
	
		char *sjb = str2;
		short int *sjb1 = (short int*)sjb;
		*sjb1 = htons(3);

		short int *sjb2 = (short int*)(sjb+2);
		*sjb2 = htons(num);

		char *sjb3 = (str2+4);

		rts = read(fd,sjb3,512);
		printf("%ld\n",rts);
		int sjblen = rts+4;
		//发送数据包
		if(sendto(sfd,str2,sjblen,0,(struct sockaddr*)&rcv_addrmsg,addrlen) < 0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		printf("sendto success\n");

        //接收ACK
		bzero(str1,sizeof(str1));
		if(recvfrom(sfd,str1,sizeof(str1),0,(struct sockaddr*)&rcv_addrmsg,&addrlen) < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}

		//判断ACK操作码是否为5,为5是错误报
		char *ack = str1;
		short int *err;
		err = (short int*)ack;
		if(ntohs(*err) == 5)
		{
			printf("error:%s\n",(char*)(err+2));
			return -1;
		}

		//操作码不为5,即为4,即是ACK,判断块编号
		short int *kbh = (short int*)str1;
		if(ntohs(*(kbh+1)) != num)
		{
			continue;
		}
		
		num++;

		if(rts != 512)
		{
			break;
		}

	}

}

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd < 0)
	{
		perror("socket");
		return -1;
	}

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(69);
	sin.sin_addr.s_addr = inet_addr(IP);

	int a = 0;
	while(1)
	{
		printf("**************\n");
		printf("**  1.下载  **\n");
		printf("**  2.上传  **\n");
		printf("**  3.退出  **\n");
		printf("请选择你的数字!>>>\n");
		scanf("%d",&a);

		switch(a)
		{
		case 1:
			do_download(sfd,sin);
			break;
		case 2:
			do_upload(sfd,sin);
			break;
		case 3:
			goto END;
		default:
			printf("你是猪??????\n");
		}

	}

END:
	close(sfd);

	return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值