作业8.10 TFTP协议 下载功能

TFTP协议 下载功能

实现代码

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

//打印错误新的宏函数
#define ERR_MSG(msg)  do{\
	fprintf(stderr, " __%d__ ", __LINE__);\
	perror(msg);\
}while(0)

int main(int argc, const char *argv[])
{
	if(argc < 4)
	{
		fprintf(stderr, "请输入IP port\n");
		return -1;
	}
	//将获取到的端口号字符串,转换成整形
	int port = atoi(argv[2]);

	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}	

	//绑定客户端自身的地址信息结构体 ---> 非必须绑定 
	
	//填充服务器的IP地址以及端口号 -->因为客户端要主动发送数据包给服务器
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET;
	sin.sin_port 		= htons(port);
	sin.sin_addr.s_addr = inet_addr(argv[1]);

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

	char buf[128] = "";         //读写请求
	char buf2[1024]= "";        //数据包
	char buf3[16]="";           //ACK
	char *ptr=buf;
	short int* pa =(short int*)ptr;
	*pa=htons(1);               //读写请求的操作码
	char *pb=ptr+2;             //读写请求的文件名
	strcpy(pb,"5.png");
	char *pc =pb+strlen(pb);
	char *pd=pc+1;
	strcpy(pd,"octet");         //读写请求的模式
	size_t size=2+strlen(pb)+1+strlen("octet")+1;   //读写请求长度

	//将数据包发送给服务器,所以地址信息结构体需要填服务器的信息
	if(sendto(sfd, buf,size,0,(struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	//printf("sendto success\n");
	
	short int num=1;            //块编号
	ssize_t res;                //数据包成功读取的字节数
	int fd=open(argv[3],O_WRONLY|O_TRUNC|O_CREAT,0777);     //打开指定文件
	if(fd<0)
	{
		ERR_MSG("open");
		return -1;
	}

	while(1)
	{
		bzero(buf2, sizeof(buf2));     // 数据包清空

		//接收服务器发送过来的数据包
		if((res=recvfrom(sfd, buf2, sizeof(buf2), 0, (struct sockaddr*)&rcv_addrmsg,&addrlen))< 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		char *ptr2=buf2;
		short int *err;
		if(ntohs(*(err=(short int*)ptr2))==5)  //操作码等于5,ERROR
		{
			printf("error:%s\n",(char*)(err+2));       //打印差错信息
			return -1;
		}

		//操作码不为5,即为3,是数据包,ptr2指向数据包首地址
		write(fd,ptr2+4,res-4);             //数据包的数据写入文件
		short int *pa2=(short int*)ptr2;    //获取服务器发过来的块编号
		if(ntohs(*(pa2+1))!=num)            //比较块编号是否相同,即顺序正确
		{
			continue;
		}
		num++;
		
		bzero(buf3, sizeof(buf3));       //清空ACK
		char *ptr3=buf3;                 //ACK首地址
		short int* pa3=(short int*)ptr3;
		*pa3=htons(4);                   //ACK操作码
		*(pa3+1)=*(pa2+1);               //ACK块编号

		//将ACK发送给服务器
		if(sendto(sfd, buf3,4,0,(struct sockaddr*)&rcv_addrmsg, addrlen) < 0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		
		if(res<516)      //数据包里的数据小于512字节
		{
			break;                //退出循环
		}
	}
	printf("下载完成\n");
	//关闭套接字
	close(sfd);
	return 0;
}

运行结果

服务器
IP地址:192.168.31.22
端口:69
从服务器下载文件5.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不知名大学生M

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值