2022.8.10 作业

1、实现TFTP的客户端,下载服务器的文件

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

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

#define N 600

int main(int argc, const char *argv[])
{
	if(argc<3){
		fprintf(stderr,"请传参:%s ip filename\n",argv[0]);
		return -1;
	}

	//创建套接字
	int cfd=socket(AF_INET,SOCK_DGRAM,0);
	if(cfd<0){
		ERR_MSG("socket");
		return -1;
	}
	printf("socket success\n");

	//允许端口快速重用
	int reuse;
	if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0){
		ERR_MSG("setsockopt");
		return -1;
	}

	//填充地址结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(69);
	sin.sin_addr.s_addr=inet_addr(argv[1]);

	//保存与客户端通信的目标地址信息结构体
	struct sockaddr_in tar;
	socklen_t addrlen=sizeof(tar);

	//使用字符数组当消息包载体
	//组一个下载请求包
	char buf[N]={0};
	char *prt=buf;
	int len;
	*(short *)prt=htons(1);
	prt=buf+2;
	strcpy(prt,argv[2]);
	prt+=strlen(prt)+1;
	strcpy(prt,"octet");
	prt+=strlen(prt);
	len=prt-buf+1;

	//给服务器发送下载请求
	if(sendto(cfd,buf,len,0,(struct sockaddr *)&sin,sizeof(sin))<0){
		ERR_MSG("sendto");
		return -1;
	}

	//在本地创建一个文件保存下载的文件内容
	int fd=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0664);
	if(fd<0){
		ERR_MSG("open");
		return -1;
	}
	
	//下载文件
	ssize_t res=0;
	//记录消息包的编号
	short num=1;
	while(1){
		//接受服务器的数据包
		res=recvfrom(cfd,buf,516,0,(struct sockaddr *)&tar,&addrlen);
		if(res<0){
			ERR_MSG("recvfrom");
			break;
		}

		//判断数据包是否有误
		if(ntohs(*(short *)buf)!=3){
			fprintf(stderr,"%s",buf+4);
			continue;
		}

		//判断数据包编号是否连续
		if(ntohs(*(short *)(buf+2))!=num){
			continue;
		}
		
		//写入文件
		write(fd,buf+4,res-4);

		//判断是否下载完毕
		if(res<516){
			printf("下载完毕\n");
			break;	
		}
		
		//组ACK
		*(short *)buf=htons(4);
		*(short *)(buf+2)=htons(num++);

		//向服务器发送ACK,表示想要接受下一个消息包
		if(sendto(cfd,buf,4,0,(struct sockaddr *)&tar,addrlen)<0){
			ERR_MSG("sendto");
			break;
		}
	}

	//关闭套接字和文件描述符
	close(cfd);
	close(fd);

	return 0;
}

测试:

2、实现TFTP的客户端,上传本地文件到服务器

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

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

#define N 600

int main(int argc, const char *argv[])
{
	if(argc<3){
		fprintf(stderr,"请传参:%s ip filename\n",argv[0]);
		return -1;
	}

	//创建套接字
	int cfd=socket(AF_INET,SOCK_DGRAM,0);
	if(cfd<0){
		ERR_MSG("socket");
		return -1;
	}
	printf("socket success\n");

	//允许端口快速重用
	int reuse;
	if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0){
		ERR_MSG("setsockopt");
		return -1;
	}

	//填充地址结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(69);
	sin.sin_addr.s_addr=inet_addr(argv[1]);

	//保存与客户端通信的目标地址信息结构体
	struct sockaddr_in tar;
	socklen_t addrlen=sizeof(tar);

	//使用字符数组当消息包载体
	//组一个上传请求包
	char buf[N]={0};
	char *prt=buf;
	int len;
	*(short *)prt=htons(2);
	prt=buf+2;
	strcpy(prt,argv[2]);
	prt+=strlen(prt)+1;
	strcpy(prt,"octet");
	prt+=strlen(prt);
	len=prt-buf+1;

	//给服务器发送上传请求
	if(sendto(cfd,buf,len,0,(struct sockaddr *)&sin,sizeof(sin))<0){
		ERR_MSG("sendto");
		return -1;
	}

	//接受服务器的消息,获取临时ip与端口号
	if(recvfrom(cfd,buf,4,0,(struct sockaddr *)&sin,&addrlen)<0){
		ERR_MSG("recvfrom");
		return -1;
	}

	//打开本地要上传到服务器的文件
	int fd=open(argv[2],O_RDONLY);
	if(fd<0){
		ERR_MSG("open");
		return -1;
	}
	
	//上传文件
	ssize_t res=0;
	//记录消息包的编号
	short num=1;
	while(1){
		bzero(buf,sizeof(buf));

		//组消息包
		*(short *)buf=htons(3);
		*(short *)(buf+2)=htons(num);

		//从本地文件中读数据存到消息包中
		res=read(fd,buf+4,512);
		if(res<0){
			ERR_MSG("read");
			break;
		}

TRY:
		//上传消息包
		if(sendto(cfd,buf,res+4,0,(struct sockaddr *)&sin,sizeof(sin))<0){
			ERR_MSG("sendto");
			break;
		}

		//接受服务器的ACK
		if(recvfrom(cfd,buf,4,0,(struct sockaddr *)&sin,&addrlen)<0){
			ERR_MSG("recvfrom");
			break;
		}
		
		//判断ACK是否有误
		if(ntohs(*(short *)buf)!=4){
			goto TRY;	
		}

		if(ntohs(*(short *)(buf+2))!=num){
			goto TRY;
		}
		
		//判断是否上传完毕
		if(res<512){
			printf("上传完毕\n");
			break;
		}
		num++;
	}

	//关闭套接字和文件描述符
	close(cfd);
	close(fd);

	return 0;
}

测试:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值